From d51277235bad7f3579a0a5c9a24af71eb9ce6629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Tue, 20 Mar 2018 12:01:08 +0100 Subject: [PATCH 01/87] Use correct type for read(2)'ing. --- src/device_serial.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/device_serial.c b/src/device_serial.c index 4e0ddd5..c75c1a1 100644 --- a/src/device_serial.c +++ b/src/device_serial.c @@ -26,7 +26,8 @@ device_serial_read_1(void *vd, uint16_t offset) { device_t *d; struct device_serial_priv *dp; - uint8_t val, nread; + ssize_t nread; + uint8_t val; d = (device_t *) vd; dp = d->aux; From 065d001135a620fc95f26359e137b59872ce1ddf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Tue, 20 Mar 2018 12:06:40 +0100 Subject: [PATCH 02/87] Add missing header file. --- test/utils.c | 1 + 1 file changed, 1 insertion(+) diff --git a/test/utils.c b/test/utils.c index b9d7e93..ff7bc34 100644 --- a/test/utils.c +++ b/test/utils.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "bus.h" From 47aca32da73a7e6c8459adf0b432c10288ba839d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Wed, 21 Mar 2018 16:22:29 +0100 Subject: [PATCH 03/87] Add minimal logging functionality. --- src/Makefile | 3 ++- src/log.c | 37 +++++++++++++++++++++++++++++++++++++ src/log.h | 14 ++++++++++++++ src/rk65c02.c | 3 +++ 4 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 src/log.c create mode 100644 src/log.h diff --git a/src/Makefile b/src/Makefile index ea7ee87..cd01075 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,13 +1,14 @@ CLI=rk65c02cli CLI_OBJS=rk65c02cli.o -LIB_OBJS=rk65c02.o bus.o instruction.o emulation.o debug.o device_ram.o device_serial.o +LIB_OBJS=rk65c02.o bus.o instruction.o emulation.o debug.o device_ram.o device_serial.o log.o LIB_SO=librk65c02.so LIB_STATIC=librk65c02.a LDFLAGS_SO=-shared LDFLAGS_CLI=-lreadline CFLAGS=-Wall -fpic -ggdb +#CFLAGS=-Wall -fpic -ggdb -I/opt/local/include/uthash 65C02ISA=65c02isa EMULATION=emulation diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..15dde25 --- /dev/null +++ b/src/log.c @@ -0,0 +1,37 @@ +#include +#include + +#include "log.h" + +static const char *level_str[] = { + "", + "CRITICAL", + "ERROR", + "INFO", + "DEBUG", + "TRACE" +}; + +static uint8_t level = LOG_INFO; + +void rk6502_loglevel_set(uint8_t l) +{ + level = l; +} + +void rk6502_log(uint8_t l, const char* fmt, ...) +{ + va_list args; + + if (l > level) + return; + + fprintf(stderr, "%s:\t", level_str[l]); + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + fprintf(stderr, "\n"); +} + diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..99c0eac --- /dev/null +++ b/src/log.h @@ -0,0 +1,14 @@ +#include + +#define LOG_TRACE 5 +#define LOG_DEBUG 4 +#define LOG_INFO 3 +#define LOG_ERROR 2 +#define LOG_CRIT 1 +#define LOG_NOTHING 0 /* At 0 nothing will get logged, can be set as + current level, but not when creating new log + messages. */ + +void rk6502_loglevel_set(uint8_t); +void rk6502_log(uint8_t, const char *, ...); + diff --git a/src/rk65c02.c b/src/rk65c02.c index 2a766a8..8b95f06 100644 --- a/src/rk65c02.c +++ b/src/rk65c02.c @@ -9,6 +9,7 @@ #include "bus.h" #include "instruction.h" #include "rk65c02.h" +#include "log.h" #include "debug.h" void rk65c02_exec(rk65c02emu_t *); @@ -34,6 +35,8 @@ rk65c02_init(bus_t *b) e.trace_head = NULL; e.runtime_disassembly = false; + rk6502_log(LOG_DEBUG, "Initialized new emulator."); + return e; } From 5cb6f3488e37ac5f695078cebf8447e370fdd1f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Thu, 22 Mar 2018 15:08:51 +0100 Subject: [PATCH 04/87] Further improvements and refactoring to logging. --- src/bus.c | 14 ++++++++++---- src/debug.c | 3 ++- src/instruction.c | 15 +++++++++------ src/log.c | 8 +++++++- src/log.h | 1 + src/rk65c02.c | 4 ++-- test/test_debug.c | 4 ++++ 7 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/bus.c b/src/bus.c index c3bf3ef..95e8f0b 100644 --- a/src/bus.c +++ b/src/bus.c @@ -2,15 +2,18 @@ #include #include #include +#include #include #include #include +#include #include #include #include "bus.h" +#include "log.h" #include "device_ram.h" @@ -68,7 +71,7 @@ bus_access_device(bus_t *t, uint16_t addr, device_t **d, uint16_t *off) } if (*d == NULL) { - fprintf(stderr, "Hitting unmapped bus space @ %x!", addr); + rk6502_log(LOG_WARN, "Hitting unmapped bus space @ %x!", addr); return; } @@ -90,7 +93,8 @@ bus_read_1(bus_t *t, uint16_t addr) val = d->read_1(d, off); if (t->access_debug) - printf("bus READ @ %x (off %x) value %x\n", addr, off, val); + rk6502_log(LOG_DEBUG, "bus READ @ %x (off %x) value %x\n", + addr, off, val); return val; } @@ -104,7 +108,8 @@ bus_write_1(bus_t *t, uint16_t addr, uint8_t val) bus_access_device(t, addr, &d, &off); if (t->access_debug) - printf("bus WRITE @ %x (off %x) value %x\n", addr, off, val); + rk6502_log(LOG_DEBUG, "bus WRITE @ %x (off %x) value %x\n", + addr, off, val); d->write_1(d, off, val); } @@ -158,7 +163,8 @@ bus_load_file(bus_t *t, uint16_t addr, const char *filename) fd = open(filename, O_RDONLY); if (fd == -1) { - perror("Problem while trying to open file"); + rk6502_log(LOG_ERROR, "Problem while trying to open file: %s", + strerror(errno)); return false; } diff --git a/src/debug.c b/src/debug.c index 90cdfdc..950c519 100644 --- a/src/debug.c +++ b/src/debug.c @@ -4,6 +4,7 @@ #include "rk65c02.h" #include "instruction.h" +#include "log.h" #include "debug.h" void @@ -26,7 +27,7 @@ debug_trace_print_all(rk65c02emu_t *e) i.op1 = tr->op1; i.op2 = tr->op2; - printf("TRACE %X:\t", tr->address); + rk6502_log(LOG_TRACE, " %X:\t", tr->address); instruction_print(&i); printf("\t"); rk65c02_dump_regs(tr->regs); diff --git a/src/instruction.c b/src/instruction.c index 19af558..0f6927b 100644 --- a/src/instruction.c +++ b/src/instruction.c @@ -9,6 +9,7 @@ #include "bus.h" #include "rk65c02.h" #include "65c02isa.h" +#include "log.h" #include "instruction.h" instruction_t @@ -172,14 +173,16 @@ assemble_single_buf(uint8_t **buf, uint8_t *bsize, const char *mnemonic, address } if (!found) { - fprintf(stderr, "Couldn't find opcode for mnemonic %s mode %x\n", mnemonic, mode); + rk6502_log(LOG_ERROR, + "Couldn't find opcode for mnemonic %s mode %x.", + mnemonic, mode); return false; } *bsize = id.size; *buf = malloc(id.size); if(*buf == NULL) { - fprintf(stderr, "Error allocating assembly buffer\n"); + rk6502_log(LOG_ERROR, "Error allocating assembly buffer."); return false; } @@ -301,8 +304,8 @@ instruction_data_write_1(rk65c02emu_t *e, instrdef_t *id, instruction_t *i, uint * PC which is handled within emulation of a given opcode. */ default: - printf("unhandled addressing mode for opcode %x\n", - i->opcode); + rk6502_log(LOG_ERROR, + "unhandled addressing mode for opcode %x\n", i->opcode); break; } } @@ -368,8 +371,8 @@ instruction_data_read_1(rk65c02emu_t *e, instrdef_t *id, instruction_t *i) * PC which is handled within emulation of a given opcode. */ default: - printf("unhandled addressing mode for opcode %x\n", - i->opcode); + rk6502_log(LOG_ERROR, + "unhandled addressing mode for opcode %x\n", i->opcode); break; } diff --git a/src/log.c b/src/log.c index 15dde25..650b738 100644 --- a/src/log.c +++ b/src/log.c @@ -1,6 +1,8 @@ #include #include +#include + #include "log.h" static const char *level_str[] = { @@ -22,11 +24,15 @@ void rk6502_loglevel_set(uint8_t l) void rk6502_log(uint8_t l, const char* fmt, ...) { va_list args; + struct timeval t; + + gettimeofday(&t, NULL); if (l > level) return; - fprintf(stderr, "%s:\t", level_str[l]); + fprintf(stderr, "%ld %s:\t", (t.tv_sec * 1000000 + t.tv_usec), + level_str[l]); va_start(args, fmt); vfprintf(stderr, fmt, args); diff --git a/src/log.h b/src/log.h index 99c0eac..738ff8b 100644 --- a/src/log.h +++ b/src/log.h @@ -3,6 +3,7 @@ #define LOG_TRACE 5 #define LOG_DEBUG 4 #define LOG_INFO 3 +#define LOG_WARN 4 #define LOG_ERROR 2 #define LOG_CRIT 1 #define LOG_NOTHING 0 /* At 0 nothing will get logged, can be set as diff --git a/src/rk65c02.c b/src/rk65c02.c index 8b95f06..04b6af6 100644 --- a/src/rk65c02.c +++ b/src/rk65c02.c @@ -119,8 +119,8 @@ rk65c02_exec(rk65c02emu_t *e) if (!instruction_modify_pc(&id)) program_counter_increment(e, &id); } else { - printf("unimplemented opcode %X @ %X\n", i.opcode, - e->regs.PC); + rk6502_log(LOG_ERROR, "unimplemented opcode %X @ %X\n", + i.opcode, e->regs.PC); e->state = STOPPED; e->stopreason = EMUERROR; } diff --git a/test/test_debug.c b/test/test_debug.c index f3231e2..fde4017 100644 --- a/test/test_debug.c +++ b/test/test_debug.c @@ -12,6 +12,7 @@ #include "instruction.h" #include "debug.h" #include "utils.h" +#include "log.h" ATF_TC_WITHOUT_HEAD(breakpoint); ATF_TC_BODY(breakpoint, tc) @@ -54,10 +55,13 @@ ATF_TC_BODY(trace, tc) trace_t *tr; int i; + rk6502_loglevel_set(LOG_TRACE); + b = bus_init_with_default_devs(); a = assemble_init(&b, ROM_LOAD_ADDR); e = rk65c02_init(&b); + e.regs.PC = ROM_LOAD_ADDR; debug_trace_set(&e, true); From 0c77eeb505f92a8a2268aa04f7aa7af55bdaf142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Fri, 23 Mar 2018 11:23:16 +0100 Subject: [PATCH 05/87] Remove leftover debug code. --- src/emulation.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/emulation.c b/src/emulation.c index 949e1b9..2c6092e 100644 --- a/src/emulation.c +++ b/src/emulation.c @@ -238,9 +238,6 @@ emul_bbs7(rk65c02emu_t *e, void *id, instruction_t *i) void emul_bit(rk65c02emu_t *e, void *id, instruction_t *i) { -/* uint8_t v = instruction_data_read_1(e, (instrdef_t *) id, i); - printf("%x\n", v);*/ - /* zero flag set if acculumator AND memory equals zero */ if (e->regs.A & instruction_data_read_1(e, (instrdef_t *) id, i)) e->regs.P &= ~P_ZERO; From 5fddf2c5dcfdf926440f07994a5cb20db441738f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Fri, 23 Mar 2018 13:37:07 +0100 Subject: [PATCH 06/87] More refactoring and improvements to logging! --- src/bus.c | 11 ++++--- src/debug.c | 13 ++++++--- src/instruction.c | 65 ++++++++++++++++++++++++++++------------- src/instruction.h | 1 + src/log.c | 4 +-- src/log.h | 4 +-- src/rk65c02.c | 73 +++++++++++++++++++++-------------------------- src/rk65c02.h | 3 +- test/test_debug.c | 2 +- 9 files changed, 102 insertions(+), 74 deletions(-) diff --git a/src/bus.c b/src/bus.c index 95e8f0b..6a508d2 100644 --- a/src/bus.c +++ b/src/bus.c @@ -33,6 +33,9 @@ bus_device_add(bus_t *b, device_t *d, uint16_t addr) dm->addr = addr; LL_APPEND((b->dm_head), dm); + + rk65c02_log(LOG_INFO, "Bus mapping added: %x device %s size %x.", + addr, d->name, d->size); } void @@ -71,7 +74,7 @@ bus_access_device(bus_t *t, uint16_t addr, device_t **d, uint16_t *off) } if (*d == NULL) { - rk6502_log(LOG_WARN, "Hitting unmapped bus space @ %x!", addr); + rk65c02_log(LOG_WARN, "Hitting unmapped bus space @ %x!", addr); return; } @@ -93,7 +96,7 @@ bus_read_1(bus_t *t, uint16_t addr) val = d->read_1(d, off); if (t->access_debug) - rk6502_log(LOG_DEBUG, "bus READ @ %x (off %x) value %x\n", + rk65c02_log(LOG_DEBUG, "bus READ @ %x (off %x) value %x\n", addr, off, val); return val; @@ -108,7 +111,7 @@ bus_write_1(bus_t *t, uint16_t addr, uint8_t val) bus_access_device(t, addr, &d, &off); if (t->access_debug) - rk6502_log(LOG_DEBUG, "bus WRITE @ %x (off %x) value %x\n", + rk65c02_log(LOG_DEBUG, "bus WRITE @ %x (off %x) value %x\n", addr, off, val); d->write_1(d, off, val); @@ -163,7 +166,7 @@ bus_load_file(bus_t *t, uint16_t addr, const char *filename) fd = open(filename, O_RDONLY); if (fd == -1) { - rk6502_log(LOG_ERROR, "Problem while trying to open file: %s", + rk65c02_log(LOG_ERROR, "Problem while trying to open file: %s", strerror(errno)); return false; } diff --git a/src/debug.c b/src/debug.c index 950c519..81371c3 100644 --- a/src/debug.c +++ b/src/debug.c @@ -18,6 +18,8 @@ debug_trace_print_all(rk65c02emu_t *e) { trace_t *tr; instruction_t i; + char *instrstr; + char *regsstr; if (e->trace_head == NULL) return; @@ -26,11 +28,14 @@ debug_trace_print_all(rk65c02emu_t *e) i.opcode = tr->opcode; i.op1 = tr->op1; i.op2 = tr->op2; + instrstr = instruction_string_get(&i); + regsstr = rk65c02_regs_string_get(tr->regs); - rk6502_log(LOG_TRACE, " %X:\t", tr->address); - instruction_print(&i); - printf("\t"); - rk65c02_dump_regs(tr->regs); + rk65c02_log(LOG_TRACE, "%X: %s\t%s", tr->address, instrstr, + regsstr); + + free(instrstr); + free(regsstr); } } diff --git a/src/instruction.c b/src/instruction.c index 0f6927b..5dab91f 100644 --- a/src/instruction.c +++ b/src/instruction.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -55,59 +56,83 @@ instruction_fetch(bus_t *b, uint16_t addr) void instruction_print(instruction_t *i) { + char *str; + + str = instruction_string_get(i); + + printf("%s", str); + + free(str); +} + +char * +instruction_string_get(instruction_t *i) +{ +#define INSTR_STR_LEN 16 instrdef_t id; + char *str; + + str = malloc(INSTR_STR_LEN); + if (str == NULL) { + rk65c02_log(LOG_CRIT, "Error allocating memory for buffer: %s.", + strerror(errno)); + return NULL; + } + memset(str, 0, INSTR_STR_LEN); id = instruction_decode(i->opcode); switch (id.mode) { case IMPLIED: - printf("%s", id.mnemonic); + snprintf(str, INSTR_STR_LEN, "%s", id.mnemonic); break; case ACCUMULATOR: - printf("%s A", id.mnemonic); + snprintf(str, INSTR_STR_LEN, "%s A", id.mnemonic); break; case IMMEDIATE: - printf("%s #%#02x", id.mnemonic, i->op1); + snprintf(str, INSTR_STR_LEN, "%s #%#02x", id.mnemonic, i->op1); break; case ZP: - printf("%s %#02x", id.mnemonic, i->op1); + snprintf(str, INSTR_STR_LEN, "%s %#02x", id.mnemonic, i->op1); break; case ZPX: - printf("%s %#02x,X", id.mnemonic, i->op1); + snprintf(str, INSTR_STR_LEN, "%s %#02x,X", id.mnemonic, i->op1); break; case ZPY: - printf("%s %#02x,Y", id.mnemonic, i->op1); + snprintf(str, INSTR_STR_LEN, "%s %#02x,Y", id.mnemonic, i->op1); break; case IZP: - printf("%s (%#02x)", id.mnemonic, i->op1); + snprintf(str, INSTR_STR_LEN, "%s (%#02x)", id.mnemonic, i->op1); break; case IZPX: - printf("%s (%#02x,X)", id.mnemonic, i->op1); + snprintf(str, INSTR_STR_LEN, "%s (%#02x,X)", id.mnemonic, i->op1); break; case IZPY: - printf("%s (%#02x),Y", id.mnemonic, i->op1); + snprintf(str, INSTR_STR_LEN, "%s (%#02x),Y", id.mnemonic, i->op1); break; case ZPR: - printf("%s %#02x,%#02x", id.mnemonic, i->op1, i->op2); + snprintf(str, INSTR_STR_LEN, "%s %#02x,%#02x", id.mnemonic, i->op1, i->op2); break; case ABSOLUTE: - printf("%s %#02x%02x", id.mnemonic, i->op2, i->op1); + snprintf(str, INSTR_STR_LEN, "%s %#02x%02x", id.mnemonic, i->op2, i->op1); break; case ABSOLUTEX: - printf("%s %#02x%02x,X", id.mnemonic, i->op2, i->op1); + snprintf(str, INSTR_STR_LEN, "%s %#02x%02x,X", id.mnemonic, i->op2, i->op1); break; case ABSOLUTEY: - printf("%s %#02x%02x,Y", id.mnemonic, i->op2, i->op1); + snprintf(str, INSTR_STR_LEN, "%s %#02x%02x,Y", id.mnemonic, i->op2, i->op1); break; case IABSOLUTE: - printf("%s (%#02x%02x)", id.mnemonic, i->op2, i->op1); + snprintf(str, INSTR_STR_LEN, "%s (%#02x%02x)", id.mnemonic, i->op2, i->op1); break; case IABSOLUTEX: - printf("%s (%#02x%02x,X)", id.mnemonic, i->op2, i->op1); + snprintf(str, INSTR_STR_LEN, "%s (%#02x%02x,X)", id.mnemonic, i->op2, i->op1); break; case RELATIVE: - printf("%s %#02x", id.mnemonic, i->op1); + snprintf(str, INSTR_STR_LEN, "%s %#02x", id.mnemonic, i->op1); break; } + + return str; } assembler_t @@ -173,7 +198,7 @@ assemble_single_buf(uint8_t **buf, uint8_t *bsize, const char *mnemonic, address } if (!found) { - rk6502_log(LOG_ERROR, + rk65c02_log(LOG_ERROR, "Couldn't find opcode for mnemonic %s mode %x.", mnemonic, mode); return false; @@ -182,7 +207,7 @@ assemble_single_buf(uint8_t **buf, uint8_t *bsize, const char *mnemonic, address *bsize = id.size; *buf = malloc(id.size); if(*buf == NULL) { - rk6502_log(LOG_ERROR, "Error allocating assembly buffer."); + rk65c02_log(LOG_ERROR, "Error allocating assembly buffer."); return false; } @@ -304,7 +329,7 @@ instruction_data_write_1(rk65c02emu_t *e, instrdef_t *id, instruction_t *i, uint * PC which is handled within emulation of a given opcode. */ default: - rk6502_log(LOG_ERROR, + rk65c02_log(LOG_ERROR, "unhandled addressing mode for opcode %x\n", i->opcode); break; } @@ -371,7 +396,7 @@ instruction_data_read_1(rk65c02emu_t *e, instrdef_t *id, instruction_t *i) * PC which is handled within emulation of a given opcode. */ default: - rk6502_log(LOG_ERROR, + rk65c02_log(LOG_ERROR, "unhandled addressing mode for opcode %x\n", i->opcode); break; } diff --git a/src/instruction.h b/src/instruction.h index 0fd11df..98595f0 100644 --- a/src/instruction.h +++ b/src/instruction.h @@ -51,6 +51,7 @@ typedef struct assembler assembler_t; instruction_t instruction_fetch(bus_t *, uint16_t); instrdef_t instruction_decode(uint8_t); void instruction_print(instruction_t *); +char * instruction_string_get(instruction_t *); void disassemble(bus_t *, uint16_t); uint8_t instruction_data_read_1(rk65c02emu_t *, instrdef_t *, instruction_t *); void instruction_data_write_1(rk65c02emu_t *, instrdef_t *, instruction_t *, uint8_t); diff --git a/src/log.c b/src/log.c index 650b738..3ad117f 100644 --- a/src/log.c +++ b/src/log.c @@ -16,12 +16,12 @@ static const char *level_str[] = { static uint8_t level = LOG_INFO; -void rk6502_loglevel_set(uint8_t l) +void rk65c02_loglevel_set(uint8_t l) { level = l; } -void rk6502_log(uint8_t l, const char* fmt, ...) +void rk65c02_log(uint8_t l, const char* fmt, ...) { va_list args; struct timeval t; diff --git a/src/log.h b/src/log.h index 738ff8b..3b50e7f 100644 --- a/src/log.h +++ b/src/log.h @@ -10,6 +10,6 @@ current level, but not when creating new log messages. */ -void rk6502_loglevel_set(uint8_t); -void rk6502_log(uint8_t, const char *, ...); +void rk65c02_loglevel_set(uint8_t); +void rk65c02_log(uint8_t, const char *, ...); diff --git a/src/rk65c02.c b/src/rk65c02.c index 04b6af6..92e5e48 100644 --- a/src/rk65c02.c +++ b/src/rk65c02.c @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -35,7 +36,7 @@ rk65c02_init(bus_t *b) e.trace_head = NULL; e.runtime_disassembly = false; - rk6502_log(LOG_DEBUG, "Initialized new emulator."); + rk65c02_log(LOG_DEBUG, "Initialized new emulator."); return e; } @@ -119,7 +120,7 @@ rk65c02_exec(rk65c02emu_t *e) if (!instruction_modify_pc(&id)) program_counter_increment(e, &id); } else { - rk6502_log(LOG_ERROR, "unimplemented opcode %X @ %X\n", + rk65c02_log(LOG_ERROR, "unimplemented opcode %X @ %X\n", i.opcode, e->regs.PC); e->state = STOPPED; e->stopreason = EMUERROR; @@ -188,50 +189,42 @@ rk65c02_dump_stack(rk65c02emu_t *e, uint8_t n) void rk65c02_dump_regs(reg_state_t regs) { - printf("A: %X X: %X Y: %X PC: %X SP: %X P: ", - regs.A, regs.X, regs.Y, regs.PC, regs.SP); + char *str; - if (regs.P & P_NEGATIVE) - printf("N"); - else - printf("-"); + str = rk65c02_regs_string_get(regs); - if (regs.P & P_SIGN_OVERFLOW) - printf("V"); - else - printf("-"); + printf ("%s", str); - if (regs.P & P_UNDEFINED) - printf("1"); - else - printf("-"); + free(str); +} - if (regs.P & P_BREAK) - printf("B"); - else - printf("-"); +char * +rk65c02_regs_string_get(reg_state_t regs) +{ +#define REGS_STR_LEN 50 + char *str; - if (regs.P & P_DECIMAL) - printf("D"); - else - printf("-"); + /* XXX: string allocation to a separate utility function? */ + str = malloc(REGS_STR_LEN); + if (str == NULL) { + rk65c02_log(LOG_CRIT, "Error allocating memory for buffer: %s.", + strerror(errno)); + return NULL; + } + memset(str, 0, REGS_STR_LEN); - if (regs.P & P_IRQ_DISABLE) - printf("I"); - else - printf("-"); + snprintf(str, REGS_STR_LEN, "A: %X X: %X Y: %X PC: %X SP: %X P: %c%c%c%c%c%c%c%c", + regs.A, regs.X, regs.Y, regs.PC, regs.SP, + (regs.P & P_NEGATIVE) ? 'N' : '-', + (regs.P & P_SIGN_OVERFLOW) ? 'V' : '-', + (regs.P & P_UNDEFINED) ? '1' : '-', + (regs.P & P_BREAK) ? 'B' : '-', + (regs.P & P_DECIMAL) ? 'D' : '-', + (regs.P & P_IRQ_DISABLE) ? 'I' : '-', + (regs.P & P_ZERO) ? 'Z' : '-', + (regs.P & P_CARRY) ? 'C' : '-'); - if (regs.P & P_ZERO) - printf("Z"); - else - printf("-"); - - if (regs.P & P_CARRY) - printf("C"); - else - printf("-"); - - printf("\n"); + return str; } /* int @@ -252,7 +245,7 @@ main(void) bus_write_1(&b, 8, 0x0); bus_write_1(&b, 9, OP_STP); - rk6502_start(&b, 0); + rk65c02_start(&b, 0); bus_finish(&b); } diff --git a/src/rk65c02.h b/src/rk65c02.h index 53df0ec..259b11e 100644 --- a/src/rk65c02.h +++ b/src/rk65c02.h @@ -79,7 +79,8 @@ typedef struct rk65c02emu rk65c02emu_t; rk65c02emu_t rk65c02_init(bus_t *); void rk65c02_start(rk65c02emu_t *); void rk65c02_step(rk65c02emu_t *, uint16_t); -void rk65c02_dump_regs(reg_state_t regs); +char *rk65c02_regs_string_get(reg_state_t); +void rk65c02_dump_regs(reg_state_t); void rk65c02_dump_stack(rk65c02emu_t *, uint8_t); void rk65c02_irq(rk65c02emu_t *e); diff --git a/test/test_debug.c b/test/test_debug.c index fde4017..eb93f58 100644 --- a/test/test_debug.c +++ b/test/test_debug.c @@ -55,7 +55,7 @@ ATF_TC_BODY(trace, tc) trace_t *tr; int i; - rk6502_loglevel_set(LOG_TRACE); + rk65c02_loglevel_set(LOG_TRACE); b = bus_init_with_default_devs(); a = assemble_init(&b, ROM_LOAD_ADDR); From eeb337a0d69befd37cb7d2f575b653fd508a2b73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Sat, 24 Mar 2018 00:14:41 +0100 Subject: [PATCH 07/87] Don't forget newline when printing regs. --- src/rk65c02.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rk65c02.c b/src/rk65c02.c index 92e5e48..7e2a96d 100644 --- a/src/rk65c02.c +++ b/src/rk65c02.c @@ -193,7 +193,7 @@ rk65c02_dump_regs(reg_state_t regs) str = rk65c02_regs_string_get(regs); - printf ("%s", str); + printf ("%s\n", str); free(str); } From 363bb56fc623989a838d3c3d23a6d4701e01ba0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Sat, 24 Mar 2018 23:05:28 +0100 Subject: [PATCH 08/87] Make RAM size configurable. --- src/bus.c | 4 +++- src/device_ram.c | 10 ++++------ src/device_ram.h | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/bus.c b/src/bus.c index 6a508d2..978a481 100644 --- a/src/bus.c +++ b/src/bus.c @@ -135,7 +135,7 @@ bus_init_with_default_devs() t = bus_init(); - bus_device_add(&t, device_ram_init(), 0x0); + bus_device_add(&t, device_ram_init(0xDFFF), 0x0); return t; } @@ -177,6 +177,8 @@ bus_load_file(bus_t *t, uint16_t addr, const char *filename) close(fd); + rk65c02_log(LOG_DEBUG, "Loaded file %s at %x.", filename, addr); + return true; } diff --git a/src/device_ram.c b/src/device_ram.c index c94add2..9bcbaa2 100644 --- a/src/device_ram.c +++ b/src/device_ram.c @@ -6,8 +6,6 @@ #include "bus.h" #include "device.h" -#define RAM_SIZE 0xDFFF /* should be configurable */ - uint8_t device_ram_read_1(void *, uint16_t); void device_ram_write_1(void *, uint16_t, uint8_t); @@ -36,7 +34,7 @@ device_ram_write_1(void *vd, uint16_t offset, uint8_t val) } device_t * -device_ram_init() +device_ram_init(uint16_t size) { device_t *d; @@ -45,13 +43,13 @@ device_ram_init() assert(d != NULL); d->name = "RAM"; - d->size = RAM_SIZE; + d->size = size; d->read_1 = device_ram_read_1; d->write_1 = device_ram_write_1; - d->aux = malloc(RAM_SIZE); - memset(d->aux, 0, RAM_SIZE); + d->aux = malloc(size); + memset(d->aux, 0, size); return d; } diff --git a/src/device_ram.h b/src/device_ram.h index b2fdea6..1e1019a 100644 --- a/src/device_ram.h +++ b/src/device_ram.h @@ -3,7 +3,7 @@ #include "device.h" -device_t * device_ram_init(); +device_t * device_ram_init(uint16_t); void device_ram_finish(device_t *); #endif /* _DEVICE_RAM_H_ */ From fc1503c3f06b54bc57bba49a9b93d43fd900429a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Sat, 24 Mar 2018 23:05:42 +0100 Subject: [PATCH 09/87] Add readable name for log level "none". --- src/log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/log.c b/src/log.c index 3ad117f..f8b08ee 100644 --- a/src/log.c +++ b/src/log.c @@ -6,7 +6,7 @@ #include "log.h" static const char *level_str[] = { - "", + "NONE", /* should never appear in log */ "CRITICAL", "ERROR", "INFO", From 63168d6f848ffd9af2cfb34d78dc5493979d5cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Sat, 24 Mar 2018 23:06:13 +0100 Subject: [PATCH 10/87] Increase log level for stepping test case. --- test/test_stepping.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/test_stepping.c b/test/test_stepping.c index 60ffb4c..3c148df 100644 --- a/test/test_stepping.c +++ b/test/test_stepping.c @@ -4,6 +4,8 @@ #include "bus.h" #include "rk65c02.h" +#include "log.h" + #include "utils.h" ATF_TC_WITHOUT_HEAD(step1); @@ -12,6 +14,8 @@ ATF_TC_BODY(step1, tc) rk65c02emu_t e; bus_t b; + rk65c02_loglevel_set(LOG_TRACE); + b = bus_init_with_default_devs(); e = rk65c02_init(&b); From 13a4c73807757800946595ac434a09c44eda3535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Sat, 24 Mar 2018 23:06:29 +0100 Subject: [PATCH 11/87] Unbreak BRK test case - add RAM for ISR vector. --- test/test_interrupt.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/test_interrupt.c b/test/test_interrupt.c index 5ea77e9..a895a62 100644 --- a/test/test_interrupt.c +++ b/test/test_interrupt.c @@ -8,6 +8,9 @@ #include "bus.h" #include "rk65c02.h" #include "instruction.h" +#include "log.h" +#include "device_ram.h" + #include "utils.h" #define ISR_ADDR 0xC100 @@ -23,7 +26,10 @@ ATF_TC_BODY(intr_brk, tc) rk65c02emu_t e; bus_t b; + rk65c02_loglevel_set(LOG_TRACE); + b = bus_init_with_default_devs(); + bus_device_add(&b, device_ram_init(0x100), 0xFF00); e = rk65c02_init(&b); e.regs.PC = ROM_LOAD_ADDR; From 85a53f89c810a48ad3accbeacb34564a4ced3cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Sun, 25 Mar 2018 18:01:15 +0200 Subject: [PATCH 12/87] Start implementation of display emulation. Something like a character RAM for a start. When 65C02 writes into this space, a callback will be executed that would allow redrawing the emulated screen. Multiple backends can be supported this way. --- src/Makefile | 2 +- src/device_fb.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ src/device_fb.h | 9 +++++ 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 src/device_fb.c create mode 100644 src/device_fb.h diff --git a/src/Makefile b/src/Makefile index cd01075..622a5ba 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,7 +1,7 @@ CLI=rk65c02cli CLI_OBJS=rk65c02cli.o -LIB_OBJS=rk65c02.o bus.o instruction.o emulation.o debug.o device_ram.o device_serial.o log.o +LIB_OBJS=rk65c02.o bus.o instruction.o emulation.o debug.o device_ram.o device_fb.o device_serial.o log.o LIB_SO=librk65c02.so LIB_STATIC=librk65c02.a diff --git a/src/device_fb.c b/src/device_fb.c new file mode 100644 index 0000000..3cbcf24 --- /dev/null +++ b/src/device_fb.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include + +#include "bus.h" +#include "device.h" + +#define FB_AS_SIZE 0x1000 + +#define CHAR_RAM_SIZE (80*50) /* 0xFA0 */ +#define CHAR_FONT_WIDTH 8 +#define CHAR_FONT_HEIGHT 8 + +struct fb_state { + uint8_t *cram; + + void (*fb_callback_screen_update)(uint8_t *); +}; + +uint8_t device_fb_read_1(void *, uint16_t); +void device_fb_write_1(void *, uint16_t, uint8_t); + +uint8_t +device_fb_read_1(void *vd, uint16_t offset) +{ + device_t *d; + struct fb_state *f; + + d = (device_t *) vd; + f = d->aux; + + return f->cram[offset]; +} + +void +device_fb_write_1(void *vd, uint16_t offset, uint8_t val) +{ + device_t *d; + + struct fb_state *f; + + d = (device_t *) vd; + f = d->aux; + + f->cram[offset] = val; + + if (f->fb_callback_screen_update != NULL) + f->fb_callback_screen_update(f->cram); +} + +device_t * +device_fb_init() +{ + device_t *d; + struct fb_state *f; + + d = (device_t *) malloc(sizeof(device_t)); + + assert(d != NULL); + + d->name = "FB"; + d->size = FB_AS_SIZE; + + d->read_1 = device_fb_read_1; + d->write_1 = device_fb_write_1; + + f = malloc(sizeof(struct fb_state)); + d->aux = f; + + f->cram = malloc(FB_AS_SIZE); + memset(d->aux, 0, FB_AS_SIZE); + + return d; +} + +void +device_fb_finish(device_t *d) +{ + struct fb_state *f; + + f = d->aux; + + free(f->cram); + free(f); +} + diff --git a/src/device_fb.h b/src/device_fb.h new file mode 100644 index 0000000..af9443a --- /dev/null +++ b/src/device_fb.h @@ -0,0 +1,9 @@ +#ifndef _DEVICE_RAM_H_ +#define _DEVICE_RAM_H_ + +#include "device.h" + +device_t * device_fb_init(); +void device_fb_finish(device_t *); + +#endif /* _DEVICE_RAM_H_ */ From a98c8f1018f618f52bbc02d09acad91911f62cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Mon, 26 Mar 2018 09:59:55 +0200 Subject: [PATCH 13/87] Print debug message before loading ROM. --- src/bus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bus.c b/src/bus.c index 978a481..dec03dd 100644 --- a/src/bus.c +++ b/src/bus.c @@ -164,6 +164,8 @@ bus_load_file(bus_t *t, uint16_t addr, const char *filename) int fd; uint8_t data; + rk65c02_log(LOG_DEBUG, "Loading file %s at %x.", filename, addr); + fd = open(filename, O_RDONLY); if (fd == -1) { rk65c02_log(LOG_ERROR, "Problem while trying to open file: %s", @@ -177,8 +179,6 @@ bus_load_file(bus_t *t, uint16_t addr, const char *filename) close(fd); - rk65c02_log(LOG_DEBUG, "Loaded file %s at %x.", filename, addr); - return true; } From ae3e782b74382bb343521db319905754c87562a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Mon, 26 Mar 2018 12:36:47 +0200 Subject: [PATCH 14/87] Use boehm gc to manage memory. I'm less likely to fuck this up now. --- src/bus.c | 5 ++--- src/debug.c | 9 ++++----- src/device_fb.c | 10 +++++----- src/device_ram.c | 7 ++++--- src/device_serial.c | 8 ++++---- src/instruction.c | 9 ++++----- src/rk65c02.c | 5 +++-- test/Makefile | 2 +- test/test_assemble.c | 3 --- test/test_interrupt.c | 3 --- test/utils.c | 4 +++- 11 files changed, 30 insertions(+), 35 deletions(-) diff --git a/src/bus.c b/src/bus.c index dec03dd..a0dfcde 100644 --- a/src/bus.c +++ b/src/bus.c @@ -10,6 +10,7 @@ #include +#include #include #include "bus.h" @@ -26,7 +27,7 @@ bus_device_add(bus_t *b, device_t *d, uint16_t addr) { device_mapping_t *dm; - dm = (device_mapping_t *) malloc(sizeof(device_mapping_t)); + dm = (device_mapping_t *) GC_MALLOC(sizeof(device_mapping_t)); dm->dev = d; /* TODO: check if addr + size is not bigger than RK65C02_BUS_SIZE */ @@ -186,7 +187,5 @@ void bus_finish(bus_t *t) { assert(t != NULL); - - /* TODO: foreach devices free 'em */ } diff --git a/src/debug.c b/src/debug.c index 81371c3..5225b64 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,5 +1,7 @@ #include #include + +#include #include #include "rk65c02.h" @@ -33,9 +35,6 @@ debug_trace_print_all(rk65c02emu_t *e) rk65c02_log(LOG_TRACE, "%X: %s\t%s", tr->address, instrstr, regsstr); - - free(instrstr); - free(regsstr); } } @@ -46,7 +45,7 @@ debug_trace_savestate(rk65c02emu_t *e, uint16_t address, instrdef_t *id, { trace_t *tr; - tr = (trace_t *) malloc(sizeof(trace_t)); + tr = (trace_t *) GC_MALLOC(sizeof(trace_t)); if (tr == NULL) { fprintf(stderr, "Error allocating trace structure.\n"); return; @@ -86,7 +85,7 @@ debug_breakpoint_add(rk65c02emu_t *e, uint16_t address) { breakpoint_t *bp; - bp = (breakpoint_t *) malloc(sizeof(breakpoint_t)); + bp = (breakpoint_t *) GC_MALLOC(sizeof(breakpoint_t)); if (bp == NULL) return false; diff --git a/src/device_fb.c b/src/device_fb.c index 3cbcf24..d112925 100644 --- a/src/device_fb.c +++ b/src/device_fb.c @@ -3,6 +3,8 @@ #include #include +#include + #include "bus.h" #include "device.h" @@ -55,7 +57,7 @@ device_fb_init() device_t *d; struct fb_state *f; - d = (device_t *) malloc(sizeof(device_t)); + d = (device_t *) GC_MALLOC(sizeof(device_t)); assert(d != NULL); @@ -65,10 +67,10 @@ device_fb_init() d->read_1 = device_fb_read_1; d->write_1 = device_fb_write_1; - f = malloc(sizeof(struct fb_state)); + f = GC_MALLOC(sizeof(struct fb_state)); d->aux = f; - f->cram = malloc(FB_AS_SIZE); + f->cram = GC_MALLOC(FB_AS_SIZE); memset(d->aux, 0, FB_AS_SIZE); return d; @@ -81,7 +83,5 @@ device_fb_finish(device_t *d) f = d->aux; - free(f->cram); - free(f); } diff --git a/src/device_ram.c b/src/device_ram.c index 9bcbaa2..8c07133 100644 --- a/src/device_ram.c +++ b/src/device_ram.c @@ -3,6 +3,8 @@ #include #include +#include + #include "bus.h" #include "device.h" @@ -38,7 +40,7 @@ device_ram_init(uint16_t size) { device_t *d; - d = (device_t *) malloc(sizeof(device_t)); + d = (device_t *) GC_MALLOC(sizeof(device_t)); assert(d != NULL); @@ -48,7 +50,7 @@ device_ram_init(uint16_t size) d->read_1 = device_ram_read_1; d->write_1 = device_ram_write_1; - d->aux = malloc(size); + d->aux = GC_MALLOC(size); memset(d->aux, 0, size); return d; @@ -57,6 +59,5 @@ device_ram_init(uint16_t size) void device_ram_finish(device_t *d) { - free(d->aux); } diff --git a/src/device_serial.c b/src/device_serial.c index c75c1a1..7ff652c 100644 --- a/src/device_serial.c +++ b/src/device_serial.c @@ -7,6 +7,8 @@ #include #include +#include + #include "bus.h" #include "device.h" @@ -74,7 +76,7 @@ device_serial_init() device_t *d; struct device_serial_priv *dp; - d = (device_t *) malloc(sizeof(device_t)); + d = (device_t *) GC_MALLOC(sizeof(device_t)); assert(d != NULL); @@ -84,7 +86,7 @@ device_serial_init() d->read_1 = device_serial_read_1; d->write_1 = device_serial_write_1; - dp = (struct device_serial_priv *) malloc(sizeof(struct device_serial_priv)); + dp = (struct device_serial_priv *) GC_MALLOC(sizeof(struct device_serial_priv)); d->aux = dp; if (mkfifo(txpipepath, S_IRUSR | S_IWUSR) != 0) { @@ -115,8 +117,6 @@ device_serial_finish(device_t *d) unlink(txpipepath); unlink(rxpipepath); - free(d->aux); - // XXX? } diff --git a/src/instruction.c b/src/instruction.c index 5dab91f..8554062 100644 --- a/src/instruction.c +++ b/src/instruction.c @@ -7,6 +7,8 @@ #include #include +#include + #include "bus.h" #include "rk65c02.h" #include "65c02isa.h" @@ -61,8 +63,6 @@ instruction_print(instruction_t *i) str = instruction_string_get(i); printf("%s", str); - - free(str); } char * @@ -72,7 +72,7 @@ instruction_string_get(instruction_t *i) instrdef_t id; char *str; - str = malloc(INSTR_STR_LEN); + str = GC_MALLOC(INSTR_STR_LEN); if (str == NULL) { rk65c02_log(LOG_CRIT, "Error allocating memory for buffer: %s.", strerror(errno)); @@ -164,7 +164,6 @@ assemble_single(assembler_t *a, const char *mnemonic, addressing_t mode, uint8_t return rv; rv = bus_load_buf(a->bus, a->pc, asmbuf, bsize); - free(asmbuf); a->pc += bsize; return rv; @@ -205,7 +204,7 @@ assemble_single_buf(uint8_t **buf, uint8_t *bsize, const char *mnemonic, address } *bsize = id.size; - *buf = malloc(id.size); + *buf = GC_MALLOC(id.size); if(*buf == NULL) { rk65c02_log(LOG_ERROR, "Error allocating assembly buffer."); return false; diff --git a/src/rk65c02.c b/src/rk65c02.c index 7e2a96d..cc4e406 100644 --- a/src/rk65c02.c +++ b/src/rk65c02.c @@ -7,6 +7,8 @@ #include #include +#include + #include "bus.h" #include "instruction.h" #include "rk65c02.h" @@ -195,7 +197,6 @@ rk65c02_dump_regs(reg_state_t regs) printf ("%s\n", str); - free(str); } char * @@ -205,7 +206,7 @@ rk65c02_regs_string_get(reg_state_t regs) char *str; /* XXX: string allocation to a separate utility function? */ - str = malloc(REGS_STR_LEN); + str = GC_MALLOC(REGS_STR_LEN); if (str == NULL) { rk65c02_log(LOG_CRIT, "Error allocating memory for buffer: %s.", strerror(errno)); diff --git a/test/Makefile b/test/Makefile index 80845f3..1c669c7 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,5 +1,5 @@ CFLAGS=-Wall -I../src -g -LDFLAGS=-latf-c +LDFLAGS=-latf-c -lgc RK6502LIB=../src/librk65c02.a VASM=vasm6502_std VASMFLAGS=-Fbin -c02 diff --git a/test/test_assemble.c b/test/test_assemble.c index 40c8bcc..0f18e04 100644 --- a/test/test_assemble.c +++ b/test/test_assemble.c @@ -28,20 +28,17 @@ ATF_TC_BODY(assemble_single_buf, tc) ATF_REQUIRE(assemble_single_buf_implied(&asmbuf, &bsize, "nop")); ATF_CHECK(asmbuf[0] == 0xEA); /* check if nop really */ ATF_REQUIRE(bus_load_buf(&b, caddr, asmbuf, bsize)); - free(asmbuf); caddr += bsize; ATF_REQUIRE(assemble_single_buf(&asmbuf, &bsize, "lda", IMMEDIATE, 0xAA, 0)); ATF_CHECK(asmbuf[0] == 0xA9); /* check if lda really */ ATF_CHECK(asmbuf[1] == 0xAA); /* check the operand */ ATF_REQUIRE(bus_load_buf(&b, caddr, asmbuf, bsize)); - free(asmbuf); caddr += bsize; ATF_REQUIRE(assemble_single_buf_implied(&asmbuf, &bsize, "stp")); ATF_CHECK(asmbuf[0] == 0xDB); /* check if stp really */ ATF_REQUIRE(bus_load_buf(&b, caddr, asmbuf, bsize)); - free(asmbuf); caddr += bsize; rk65c02_start(&e); diff --git a/test/test_interrupt.c b/test/test_interrupt.c index a895a62..464a833 100644 --- a/test/test_interrupt.c +++ b/test/test_interrupt.c @@ -87,17 +87,14 @@ ATF_TC_BODY(intr_rti, tc) ATF_REQUIRE(assemble_single_buf_implied(&asmbuf, &bsize, "nop")); ATF_REQUIRE(bus_load_buf(&b, israsmpc, asmbuf, bsize)); - free(asmbuf); israsmpc += bsize; ATF_REQUIRE(assemble_single_buf_implied(&asmbuf, &bsize, "rti")); ATF_REQUIRE(bus_load_buf(&b, israsmpc, asmbuf, bsize)); - free(asmbuf); israsmpc += bsize; ATF_REQUIRE(assemble_single_buf_implied(&asmbuf, &bsize, "nop")); ATF_REQUIRE(bus_load_buf(&b, ROM_LOAD_ADDR, asmbuf, bsize)); - free(asmbuf); /* There's a return address and saved processor flags on stack. */ e.regs.SP = 0xFF; diff --git a/test/utils.c b/test/utils.c index ff7bc34..7eaf446 100644 --- a/test/utils.c +++ b/test/utils.c @@ -2,7 +2,9 @@ #include #include #include + #include +#include #include "bus.h" #include "rk65c02.h" @@ -15,7 +17,7 @@ rom_path(const char *name, const atf_tc_t *tc) char *rompath; const char *srcdir; - rompath = malloc(PATH_MAX); + rompath = GC_MALLOC(PATH_MAX); srcdir = atf_tc_get_config_var(tc, "srcdir"); strcpy(rompath, srcdir); From f4aa7c4508ca06d08705a1033583be6c587c31ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Mon, 26 Mar 2018 12:38:25 +0200 Subject: [PATCH 15/87] Don't build the useless CLI for now... --- src/Makefile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Makefile b/src/Makefile index 622a5ba..17a4d30 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,12 +1,12 @@ -CLI=rk65c02cli -CLI_OBJS=rk65c02cli.o +#CLI=rk65c02cli +#CLI_OBJS=rk65c02cli.o LIB_OBJS=rk65c02.o bus.o instruction.o emulation.o debug.o device_ram.o device_fb.o device_serial.o log.o LIB_SO=librk65c02.so LIB_STATIC=librk65c02.a LDFLAGS_SO=-shared -LDFLAGS_CLI=-lreadline +#LDFLAGS_CLI=-lreadline CFLAGS=-Wall -fpic -ggdb #CFLAGS=-Wall -fpic -ggdb -I/opt/local/include/uthash @@ -17,8 +17,8 @@ DEVICE=device all : $(LIB_SO) $(LIB_STATIC) $(CLI) -$(CLI) : $(CLI_OBJS) - $(CC) -o $(CLI) $(LDFLAGS_CLI) $(CLI_OBJS) $(LIB_STATIC) +#$(CLI) : $(CLI_OBJS) +# $(CC) -o $(CLI) $(LDFLAGS_CLI) $(CLI_OBJS) $(LIB_STATIC) $(LIB_SO) : $(LIB_OBJS) $(CC) -o $(LIB_SO) $(LDFLAGS_SO) $(LIB_OBJS) @@ -38,6 +38,6 @@ $(EMULATION).h : $(65C02ISA).csv $(EMULATION).awk clean : rm -f $(65C02ISA).h $(EMULATION).h - rm -f $(LIB_OBJS) $(CLI_OBJS) - rm -f $(LIB_SO) $(LIB_STATIC) $(CLI) + rm -f $(LIB_OBJS) #$(CLI_OBJS) + rm -f $(LIB_SO) $(LIB_STATIC) #$(CLI) From afbd412879a334b9211a9e2304a6fcfbcd2e4aae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Mon, 26 Mar 2018 14:31:07 +0200 Subject: [PATCH 16/87] Remove usused variable. --- src/device_fb.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/device_fb.c b/src/device_fb.c index d112925..83c080e 100644 --- a/src/device_fb.c +++ b/src/device_fb.c @@ -79,9 +79,5 @@ device_fb_init() void device_fb_finish(device_t *d) { - struct fb_state *f; - - f = d->aux; - } From 9c166561980539f2b1a7e0742be0d4cf88958dd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Mon, 26 Mar 2018 16:11:26 +0200 Subject: [PATCH 17/87] Disable tracing by default. --- src/rk65c02.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rk65c02.c b/src/rk65c02.c index cc4e406..c7381ee 100644 --- a/src/rk65c02.c +++ b/src/rk65c02.c @@ -35,6 +35,7 @@ rk65c02_init(bus_t *b) e.irq = false; e.bps_head = NULL; + e.trace = false; e.trace_head = NULL; e.runtime_disassembly = false; From 0f0c0196e4dd5addc80c4707b573d4513522de34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Mon, 26 Mar 2018 16:28:38 +0200 Subject: [PATCH 18/87] Add logo. --- res/rk65c02.png | Bin 0 -> 40999 bytes res/rk65c02_small.png | Bin 0 -> 77778 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 res/rk65c02.png create mode 100644 res/rk65c02_small.png diff --git a/res/rk65c02.png b/res/rk65c02.png new file mode 100644 index 0000000000000000000000000000000000000000..f72648972f87db1fde09ee7a25c3475dc26c6937 GIT binary patch literal 40999 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Ç?fFUw&NxPNK#mpVy!7TQ&{-uXl>W3On~hu$)B8>dbZFGScTw(~hx*_F;-h zTWvIJ_^N8hIZIb3-R|d+)gF}2xS#7CuN%1?&x@K82HVb`o&uScEpOvXF!i(~VTwzo z5(hdTL0}4bwE~2Pt#ephJI>0gDjB}p6kD&KM}!|1$HL_Eue6{0nTwX5YrZ%Mm2N{> zw7p=tnZ61LZnB15ii<;3vE8P54(rP-SJ~xbvt@~$9|?QE+kQxapJ{tvmb*}nu7b~s z-j9is+O~H!dmbiF5M-`34_3I5*xVR5iZ3a2u=1ioEB5!KYW_k2zSr3wmq&%0*KRmL zJe#XebIs4f?A85Md&TR$P@It*(b`pUg5gkOSPsd}Q^nICt7&pc!c$9kGgS8HO6+&h zWxC3xIm6!G9-pF)%NDxa4!k!GeGcp2aE2bf{!!A7m|o(RE5hW>PIME8S;Jm^12*PB zVJGF(vOdUZx~@l@eT~on9CrH*WxXIw!XPr2YfSW@LZooZTXhl#+P?k+Y#7b?Q$h(5 zv}3OE-)XwcmWH>UPcj@UmnjT2vU^eRd{ruy#sNr&nGW}-w^i&G@Z-<>1Oi*|uhlBN z{7IU6-Ys2Wg7n){y;jTbRp#BF`H`a#GMcFv*)|g;@tC26qaKE=Ljd5$u9Wy@RkTu( z2)Q&pCZdM#yh(<$8Zs}2LH=&l7%%$ZMV>u>x2ol|sZoHw=2rW>u(uq$JV5wK z_s3bd3NFnyLn+^lmRoUXz&P8bu=kNWXHGm%UK0TmHCyuLDb0?&`(poml7st9Z8iCL zi}EG0b4>;ojq4OFyKfhaBGW)h04V34Kc_VAiL9g2q=ZTk8`nE!&t=?Hr&J~b%|W86 zFgz|`1mNR;WkLYe(+PDNoT}l>l(N?L_J^Fi`|{qq#_RNafpsT*4~*MUoSL)b_b%-_ z=iRoE_DQ}y_wt>xu^v0Mi?VcY=kKgqDowpUshWHs90`Kx4R?dvks@{B*eJmslU`B( zP$16zZBxOrX${HV&RM-fXYJMXH~4wutGlJ zlXCakX?nbVDH%@N2m(30mJ6A7)oArQI%*8Qbf)`YEL7pWd@2)E(MhE<%+Eh+1@UAo zk!B>7s?==re{{2hYJec@<|Bk8l)^;vG)>dVH5nTTWeMO*p)`>uHM7sz`YZY*>$S5R zCzn~@S3P z%H%h!@<1Dg7AZkEg_a@>S!!&{fP2@XRRAjNxJ997l-}U^NUE2>U_BrZ6#gJ*nGS&; zA>f~90#mp#1@M=;^Dyj)f3uOg`3{|k&Ia3Yef6*7eYIk(ru zgpgRNF7Rh*`cn{86Q+o_AkcCQU*5K9utr__`fqUoeih~?FsvFIQXjlyxEb``bmQw6<$jrNE+&xwcUBd{u#=k)+N}0lz zXl{fX>phtcNc*(+^I;v4yI6Nh@l%(-Hs|y-{4KWg&imvd9zLY+bs77+%$#jk8B6Ih z@z?%tY5Vp&Vd9-u|0YMIZ8gj=EG_*&XN>2cdnry@ZG02l93+>qCYamL64<C&D$SkH|dY?b!CZtMFADMlJYLupb{6R14vK8pod@4c-fod_Gq^N@@XszH{! z>J9`%2Q^CzK;fF*g>mJ{Vc!$=PG1R-Pv{mrI4DBWwcKf}61BZ9pLykET|ze!o(AA~ zZxuX2gY|_PZ`teQqi?f|BIXJ5=0+r~DT4Qn*}^>_Oo>{S*JMylf*^@3sAu28J`h2^ z=O0rMB=Rt%MXk%XY4*K7s>_=B%V1UZKQM}g_ap1K7#(t0eW(1zfbpqvd|efPi# z^6n5B^%=-F>yUK}{Qo9xxfUTW71>wBQTn9lK_rwMg`W24sTVd5s9NoZpg_O07Cca} z{M4Di$2tIKr(m0tyjQjca!z6g&ym@g_ZU&L4LY$D?63ipdye74Vdf?4A>GIasEHud z;P=t+l_Y;F$h&`{*mg7+BSXsIgcO4MyhXCHXYsDNveFb$VgzR>!$8bfmdR>BASt>O ztYH8af>d8Eh>j>F>%*aBVs^loK;9~Xo zYTN?cg>szZq1MKF;7!YK-qIg+hc;mA>s%Fs=K;rUIMQ~f_ZL@15SSL5WUON~j!XpH zZr?(E0W}f8d#w1j{ebXCAPNK>s36V)TUUg0Y1YqF82RyE0~c3k%4%$P-7*N?Vv|mLdsobz*e6S|E9gHC|5?3YbT7d01K??h5)XQZ7iF{0p)q zDVjVfic>zcAT`+03r9ywxKkM=2l_?1bSeG&yS=B`ED4vRWp@Iw*vo?<(0h z@V1Mb0q|fWzUzzNCK)Tf09sFA*qd0wh(&ND5Rv<-7RXXC(!YU5?)}Nf!Bqyno2MJV zAMBHr)eb1~XezKup!tm+^}|se4ta3&BSOS0fv?>AgQzh}mCnmGKdUr7-m4VJ8(iGy z-M?rA>@c@Px?hBgyc{S^_WM3Oe^RAuxG0~tC1bSJ8=m{gLUWdfEm$3oO0xX?M<+xO zJf7t^w3^wz-r|H{I(;aQ(3Fgvb$}^AI0tqWY* zYqah(>`ERbtvA8AXzKvt$&e8!(uTGJG*mRc%cf~QVMnc?RG}BOUenIq5d&WCTSBDL zpfEL_NZI*QZlw;Fln%lx+|vaf4JIW6kD+P0-FjaSUsl7B8e{&tEa`8%bFTs%VAi4P zo)VGuZ4P}0;9i2D`s}BAvEqlTU4JLSkOl)NQ7^B1?ELIqulZte4$nnda&B|(yt5Sr z7bCuap6ME#9Gzl1&UI97JL`pP5zRU(86G%}j7?W7n>Zko5E{J2_}c04IE4JP8FuF}wbNI7R za20Oi*jM=ZFi)f`Ju*%JwM3aj;KIjosk06t$(5noYz$s+Y&5Iew*uW3mDCU% z??J({WM3bmHO7uBI6;mk(aux}Uf8HreeMgT`(?=i>A)$r=+1c5S*PB;Pp5R6tdjW$ zB*@uWLR5?~izxXfx8-pWpyU?{#u`+|kRrrw zYGq19@+<+EX|j9voqNrD$YU%jl>6o2-VF2n2O=`lHi6{ztXTdLd=3RdsYrCuL_o1h z7|K}9DPgm(sz|AoicO342@q*|5u!_W>te8Pl2&o^R#bEB;`fqn)F~6onH-}n0V`W@ z64~`e+|U5AWN#4vx4Wd6k@Qx@F~lgsG$&=hGHU- z_#FrgDG6=he{%)e(bgaU4rpsb^9>c(sTr)JJs=bS(aIcRBW+r8B&{~B?x4w={3A<> z#zLq}i;l7wy{{g4R#d!* z`cB?KK2~EHn_)?sB)jxcLPfF){*L09nM321&xx7|hpRdAq+6w4<=J5lNPZ{i>BdgZ z`9^KUpT)r(@JJc>wNQcU_p>%bL7;SQ#tN=8I-@v8atEvC`Y_EyXZLh|c7;FosP~>J z;vg|Vr-dx2&_g+8wnU~;tMSFjERDJ@#7#o;Y*zsdt64r?{Q^4F7)OyrHFX@x>~JAR z>|R_VoB1>KxFe{IbfgVhMJ_7^lWYQxJ2sbf%KG;Z9|~Z*Ad6gwO+KmAhz{l3ULcyN z!vO+#PdzGZ0Nc1StT$voLa-`Lly}rjgmDJ81BjwcWcaIlm0a-+4R!=o4 z@2ibGl4E4tECoz~?HR(rr)*Pdghj9}gxpX-CB>h$T2cnE`xngjOf8u&Xu?N&tc}to z9}~+RTAofngI}<;N%G9`GvYH6F}lxH>-kCN&q$Zhx>UYUpo<47*E@xEuMQju&JxN7 zses(X`>>{yzwRM?s9@dr>TApR_{3-siCr*;EI?%TtSrv$l(G|glq`iY8R;WNG2-k| zFmtidSi@t-eoxXRB57WmFN1kcvH=%6A^JGj*{SlqkGsK1eb*GFZq&-;M)aB|8)ZQB zkDAu#W~-?*E?Z^q+K~JEyELTs?1^OjTG^nU z%S+S}tubILe#Yk_1{F>%D9|E*-S@zK)lQ->8dFAV2v-MeQ^`x>M{P8%FHd2Qi839JAA}*#m zAG}acLx6`8+2G&@UZw2dXU+gwpciRJ#@rKN?MVoI zSFZtKp|1$?x7-j+@8}UqnSLoZhrb5hv?`@0>ykUgkdTlN5~1ZQj0VVddOuM&6Fkz2 z7(=I$*)j;V2aU#=%oh7aV?zHCXdtjL+-N|$G&(vk1SL0WB*4eT>IVT|0C|m{Nh|2_ zDX+tuiaAvn3W42DKJ+?lwdC7icw#F#bL&E~gp9=#@!9Z=<~;2&tN3kAuQn^E0#8DU zx5iEzTrShFeZHPhxW_=(MLH1c{P7a?kn} zCkaH@1qC2Pt|Ki&g!AKiDgeOqV3ZMLE@g}F;T!xOxeHNXZ!agzb{fl>J-(Cmd=7P~&K-Kk#?g&?a)7-HK4+A@d@cGWU?P5A3o_Oh)YX;qty`G==_Xvb1o8Q(LI;w14eLMVMCIb zuzr>?_vqDR?kn_0@dYNM=gqR~7B90n|p_YfyH2FGM0EBP>73t@ow2}9je{0Rd~W&;~-{I zo0v@q;6|vz7Ut~ncILeP@uw}YnVMZ30uk_Cs5!F==2Sl+r?Cv}rihrCFt}K#F|2Td ze)GPW<7LiBYFedz@KvQs5EOa!I!)**xDF}Zd@@_9dL zPty83gcd9Qn5ZUFdcujH-qCkP(%><;F+6Y*Y0RcL6&>&>br(=CpL6k@We%ZY~nOWAez`b?(6l zPp1MNSDIE%R;)5}6WVhHfEMeDsjP1c^dS=7`7syA)^R)6REnZ|Om>bx$t6BClQ!De zPY!j2W#dO;s@oRr6O^8u$QekE=R>)Wv=>~oK>KxZw4U((IX<(#6&6Ul%&27)Oj}=a zr+{j5kACYS-A*1AgoR=_IuF=s?R*xEoPikPL>YlhNoQLisF8Fp{b?VPlZ;*-8Wmyo zq{4Gt)B@cX#zIy&wenk(anQ>gTz!yUVt1x{Hgn$GCxN_=2d?-Tst889fc|V>Eh-?ir2sL<}@#So5Z=HI7_g%|H-WoQk8((y2EOjy~SGiKx% z0m)H6lBhrG8BXB0Dcs;tljrYnuy7iKW&AOBQzTF6%->1!NuvFuyfWK#N9i)>iB%)* z_!NM?Mf<3}y9b%>u`yT7*P5gAJ8Y$1e01pKyTvtfI2A(ne%7lLT-r&Tg)ELPDBds1 z7w_Fft+KOs7w)3B>W>10kvL*nh=DmDadR}y#JwM+SL`@(>|Cr_sDmPg@P`Y-Y$}Sg zv3^kiwselZuT3}76Z`uWbh5FJGRDt+zr843?NWQFmUt|W%w$kNSJUkEx6M_%2130diyoCdWhR3jbKy=ZcEdCnP zmoVT+*4j)XG=0EQ}c%hlP_3aKTar7yW(A(MFk;o&9MOhIX;XEc^EkFbknmfk(me z{u11Y>_c*^QBI7xhCJy>Z?Lj@u^t8cdpsFw;=D@m9ipX32B0hurdW6RzNDJ5F0uX8 z`Wrw9?uqPm*&|`@n7W$q*F|9Dj&K90yz}H=*IA3j{QV(W{l7E2fWNTkjC&@J-;3q) zA%dcMFuWB6L4`1mTd*sVSOs<3%1z#h9*`7l`Gh+faY5}}J}Rt(M8E}NYWh0;BWjXO zus2u{N)=Gfuz=HPCtv>RgpPTkhl1d~&480nyWQ2g7#xnyuJVCkIx<}mr(Pbj2E;%2 zFNsy&UyyXK(SP&cOhvxtpoN-lI{XS%>lt514wq!Fp&DQS$E}_>t=C=_ki1`BbO1F@ z#2RvZm&=p1@FwaPl#mYc0qrk;|1!2j;_T91-|f)_W7JukN<&@Y?rZenT2q{*F^{)2 z$g`utYddSn+KQn``%rO^ZFObbcazN#_pWD?JXop01(CyXAtKpd!FK)g6m(b0suHWX zLPRGyn^baFG{9@EV<_$85REZ<|I&epJKg+m&rT>)vJQp<+x~!)Y@<@B<@AxR_fVoH zc+BBOrg&ov*hIm97HA2cO6B&#Q1yBNHprY@#jozKhj*04*Iac%xyE`yt$-{=P};B$ z!rCb`k@f)n>ms3X!H?hAGcuyuG_raGM&~K(>gg>xg1g7Q1#kW0c?s{@Ns4}~2}(W} zwP4gSYIZAh-qb1Tpli}(?(e1%POw$M5jN5_xaX-Y89PHEq-_T3`i|Ajr7X|hshFvB zlRG4ang9pi1aLt#c|hZ7Q$z*TZ(Mf5ni0F=$fz$^?~KrJA!)AbUz^NRhwhTrXS}7f zY4r+pwoK_!B{{}E`3Rf%za&fHa5-ViQpM9Dln<$ckm72B!nows3Hs@D?Hb;1?KTgX z>111dGE>Phq)Axvhvw*MH}d9IC1=@BB?W9GtXG6KvZXT{Eqr7`0M!ju73@*n+U+TA zdAq)DEtD*sgnDDi$N=WIN^GuBept(d$HUHIEfMA%Wj?I+epFi5k?UFLLE{U`QVJu| z$Z3y`20|zUW-*<}L70#bZjgso69&B47Hs7=@+3e3X8& z*+NUksjReE;|)NOCFq|ZVCK&i_NUh`$WJ(~800$~A8*a+zq@0#ZtcG|uP{zR9+M=< z???KMuCUbJ>!SjqRC-+9cU?|$&=nZv zH>BF0C@WA{;>#_N&rVX&ptC#T`}-#w(28A(n*0q3KG4Yi5h>L)<08{9OMMtDYEMlQ-41~3|pIy<&{nfZ$ zjf>73)xe3M1_!+Hj8iIbhTgF%Rwo&3eO&BQc#^mU4MtbCgds}*iPvAN!Ggf`Q3YdN7kgE3MSEZ8oqn-r~E)T(f)v&slN2Ss$3* zdNckv;X-H|=Z1RqoixuKH+_evH;R4yzi5_A;d&f2X30rvAK7|6wwS zxdyVcg8Z(e3xM|M6p?5hYVp!-FwU#S@&37^*!3QN;;_Ze>hB?_7cmkx{9IEiB!|y) z$C>DP{SRsv;Q*G~-xNjAs)O_6L>tRx6QgnETQ7dv9;Azn9XQwH zICjPp7ZxgQ1{9g+7Aj{T!Hf!J5Ie zwWRr6OO0~yrz^XdC%}Qfh7h3_HV_lTe~3?8tnLq`5FZKlu1bQ(Ic{&GBD^&5klJIR zpx!x}lNw|8^frzRcLw29AR4&}U5qK$E+HMt#Nqr!D|c;VcFLXm*MC#)2t$*rgR7ga88yLAOf2(t_0h?^qY zw-0YM_ioJM@!q#TWraHJsb_ZXNiX9eoNOTwJidHetJMqyq_R0^9I&X}FEQ9eS4p3x zzkffdRB~LgU{sUKqF0@lrae|YdH7p2Y9!J9lH4Z*;9okpB-UEbo9$1QV=$0LoH@{M z(mj}u<9-9E92!)B4g-$?`i0i~WTbGA5ltp$56*6=B4^Yo0T?he_P+pBWz*P+iu%h- zOX*X$V>B@c*!0TA;frdCfA%vC;A+-Ctv9dzx)84I6ZcGrn;c3S%6Q-cNbAg?IfEfu z^vP8nh_6a;7OWiP^u6b@+@(car{tsYxVlDjGjhi97#{Wz$AtL3#6l{;Vt<2#LGgOH z2&+vhsSPmop=QLQ?fVPhjM`l@Jq^uEHS-6FkPx~${(b}%3$1A~k#BR%N7%}E? z$1bTGK;d;qT|kA0?;}F}ozNKiFM}aMUi4+t`j)%2iMaF67rImWC-}4UZ@I!oi6WXc z^A?5Vk3pJ|pDY!Spd3W|!$v#uGSk7?!+pJF{yP*`HUe+W!GbC}#_jzafF_gRv!yp` zrSZKub4Q~QAfY-w9RDwRn#R%RE~l2yd%pu_tRq$E4w1o;#eR{eeEJTE#+DA+?Gs+C zXNFcZOtSiAHPUEgAlQSPCXF;s_#&9i)9q!fiPRSXMk{NUKIjRQ<36vXYtI{)q|ijd z$qjNLRwFw(Y=jtjAD$?fn%tM*D3+yWZORKkECcQ8XoX;xt<(ZRo$`Td_chyejU zWYxVrb_=<|+>$8IaF(86g_XeTK!kw^>Dv~DRRYG0b6)Lms+eQM6g%oaaFU}#Da#{9 zW9JwuPVLa(~E<=~_vznCu0d}glCHcuI`d>F4UB#N4C_2h|wtH#2=O)LAPERDB+i9i(R z1!d!3frVM%NHzdT<1rxcAIH;FB=~@7n`WYQ@XOoj^5e-dp7<3bMGh4eP4z9gtmA=q zHL-&=C(WA(90}?(0`k_P9b5{+S^tQ_k#d-Z1d|kA0~OVGh^7tn(-XPN))`t{j5`BS z;~GUf&ATQoHzA zN<1@QzLWv!fE3mCUs@$9svRbokg}eYh*=z}i4jrbr>Ip9v0$~44m~<*p zm}E#FOF~F`#LgN)Q6Vb(lk88$LBuLJeqvzeqsqE3uw$i1Dp#p0qD5O*5AI7J7u@~g z9!AU*LS48W=zIPs)-OmU_e2arp0D(slL8>fdr1ZzX z9MAh)!`_o>3MiV;G6Hdh8;wI@ti3-jBs?5{Qj7@2}6c{B7G)elRxrsr!9d zlcLI>iqeI&ENaG|rARx|7w0Ok*Dpe8SZ}(<5kV>uRdjdIf{7S-B>Q%Yt-Lo-4CL8) zS`k#?5*`Ms-UWs#Y?hzC;`}=H zIU(BBrU4CqW&`qWabOG*3>p2KD;UHKuB0JR%3@f3l*DU-2VLvvI^yqkQ+b+2^Kytv zs99Xx-Wxa@)S)|$cfe(UcS6sd>y2#xHw*B%OrC+DVRB1i`q|Y^bK|J*wteqIjegvZpDg67_U8!t^|PT#9TC-$Y1?r>BNz$!r9ctO8} z7Qz33$6jJ^SsWKkW5eS#LM%lz(5kenl9#+Y-Ig=Ym7z{yk8QD90dLiNk%uO|lPg?) ztdt3OkN!jz{`LM&#JQHIQ;ers-+lj>z0|StxUKitCm?w1h;`11n1F3r-CKiW6;D=5 zf9Xh(=GD)o88fD&B|&Y{&^#XE@;V@1H`hW16@w@B7(i6sGcE#x6fma^=-(9$WZ|Iy zORKP3EyTh^TsT^mL54i9$$Pw~$}%R-=y$@653*og{}WrQ*Lnuv^^I|yL_4hs$i8SA zOr>iTbGvLSd;i(`?Kb=V(Nk@eBApmxW*FqLcDVKk%Vk^4%Cj$q;!UO9FYh``VYG5z z`xn9Zztk7^WthlDDWydj)R@7M_o`tX62{2qVaB)}dXG3&fQbOdaFjZ@vgT<>AP@v& zLj2Ia>}28gJgsDIbfm582}RwQrehxp=Faf>-zW_%m~~F!9vE$ zo}S#*SBrf9%^)kn_xSEgT_^~%R&I(E3FXX%g}(1GIEb+R_Gw|$VJ;SQNNmLC`|%=x zfra1WQ>&YMum0=d{Le1|X|O5mzIyk++-L@*@~Aq2lGXo-NgOEs;rV7oflV^?WsZmQ zLexKald=*QIf1pi8VnH^iX)y?A~?6l45!|yAG7Rxu+X!PE3kI|F94-LTEF}5yU#_r zWgWrG%CNwdfgf7~o%$jnX=hcjSWri%rm>fh{;#yQj`kR-pjpGRHgyq%!laM_XMzIg z-}Jxk#kscL5}3xN^s^kuuP9xm(+Rb51ys&H10z~A8ig^Gaw{^tWprM3)f*x=-uO=U zVWi*4ObF)wP98dgViLJbf0m6xXe4aREO&a^yJBRj@|J4NE7J`Y)mUghj4HfzF6Hf7 zJDC1zJe7*LzF9-vTLU<_I1EGP(5zvZ-fjry9HRinZX~9h1P+X7JC2c#{(m+DtH(H2 zGt)V8ir#C)zN)NkK^&HWiMCBVnxCB z%^IY&qzITBcXT(gbx92WgcP_?D1i9{r+;l37TYKT;p{cmufEA!gO3fYDSdAZ*oN1u zoQYATe*EOY;cVBcK1>&}F^`^QFp43mcwQ|tdibd?f%!dbgqh`N6kQ}QmqLsy0qTYQ zM|u(iFH_UA3hSI&M+Q#l&d$Lr;mj;$;a;4X1DTSPVbxUO;U1aD-A{{a0$LbC3Y_~C zILl0FF#?$ZC}R<0lRH%WP#>R&io^%u-(Y0*iCOytr7WBsz-J!JC?(MK%s{`bcJDtz zV}OV6-?Jx%LuwjoBI5#?o=43+OXB(F;4|ZzgGULgt2A-6E9u4^OkDkBf45GG&)6B= zSs-%L|McLGQ#5D?v!cr^y2S!J@?0CN5tBx8D5$F73PJ)~sGark5t8WH{=NuS;h&HK z7d-`JENZg`Jx$c(vrd;PGAnnt`NBrUUkp5Pvq${CHIT@X4dtkf^-^a<*2gdTPS}whC59 z)V`4d^1%}#xDuI7xl&bEY#4~gFsnr)qlP)xnYM^h8c8rp_{@TFY+wU(r!=xmt2Jt=(%hchi)vwCRb&fQ0^ zsyLDENYrzj8mzNy8*+;ivF)5!!Cn{(aRkJ<7{Gd6=~yI}vmuVv5ap}bA?Se_+D^H8 z+&(roe&dS1Zuql_*A`9wH61 zGa$liNP%;n0_eM1-4Vo;9UQu=bHQ{Wh*rzJ;Q`#4k`>Pk{-DKJcF!ZrJmn+;Hd4^ATrUi0PY_k5rL6G0TQUggW7NAD5*;l~f4c*e9T^1{$@FeffuT)f4xTm*V zE^d(DV(i0#p7P3$Dg?YW5L>p$ur|MS*2u)1+V5LYBIR7=aCh+?r(-D>WQ>qbFrl>w}|-34%=0@F>iN z&g02Q;O#-?i{jdt^HjuDz-t)KIHFt>pEs`BxY`i}O#_&ggwwy{d&!*Or4VW{dTW?z zI)p->OGsjcW(`87@XvWi0r~8Tn;0hv(*=I@>%-VI#vfSb@f^Ey+*21dGZj39f^h+} z1{^}sWe%ZWe((S}GDr9T??smdUwM|=n6kYtNM&@+83hm;geS2vj}}jT5O(Mf3sG_i z#hN6T8bmG5EvcL0l>y1(jt~5_Wx~ zaR18X&`2ID27tjl5&$ql{XGt%kRWWbVb;LvB`+NJg=P(!MB(4_kpeQ1(3gGF1xz&> zgTS<>J}OMaxpno?Ycgx_2WZ2rL8-pXn4(5~px;wN8==33)I~-o+y(KJn4a7Toi`?9 z2J#Z^BEUKZX!)83IGe6wQJqwc$VyX(}0^syjMg1BBkK_`gTSSb* zx?-ghftyo`bV|)|21$>g$TL=fG!)a)7k4wCfGEd!^CN;LK%f#vXr46|$SgN{9t&J` zbUjme6H?%Or2u+A2k`k8$SD|PERuovZSdnwvdYjA!NV>i{^6WJG921jTmmN5pNDLQdFa2}kWy)|QgTK5vD(xFF)zo{3k8qH`(n*R zCK&Kcj*X`3B(#d^v({|EBH?{Vf%As~X1JX{7z?Im0Lu8}nDiNnA8(Qs&SPNU8Uh7L zSx@j2*>bvgcU;Rm?nInE$FitCvLQx|S7{&*fd^)pvyf~Om>P{`BP^0)%2G?}w&BHzQJPxcNyWxpD!w z37bdWb1Fz_s#LMnS_n2Z7v6@wib`;m5b!AQT;AhAYgACh@feN`)A1c0^ZkI)67@WvP8TOU~Qg@ec5_m$?;4h1m9^L9(GlOjCnq4J&X6#Y+z6bpq=9BOf;fPV2f48a9M=>(^bCi>sG`W8&;q zZTUsL8&185q;Tp5^N3kPspy+EG+rttwj|`iTzCit$rOfjl>+FeUM!6tlsBzR35<+~ zNoX(f8KCL?T12erVP}LWJR*rvOH_O#S?iYcsymo13G~ zmnR?fX0rxF%75dn0SfX1BhsR;f9Z44!Idj2NDBLqAAj<3iGW`$$qS}+ z+FX2Dq$~ej2*bktp8?@N_v2bG^qJ*c`|4knBPb|NQ@$cK_?O&WK2EKr!sn0z=OhI< z9`ztSC(%0tD*G6H zN?|z}7DYesBiOkv%t-}t@GOxHY1V+XMvXYQvzRsbT>6w+6BY_75K`duC=lSkRMg0D zB>VB>>&YSlk_6gTWU$d&svseT8AZGfx4G!TQKO^R>UI zHTtZDMw+c=4b)-LtYInBAdYkJg=IroJdQ30v|YP)k@;1A@W@a9vwg!HyWlSb%)(~~niY`?f#TftB zNB4a?*S)T<>eQ-~Sm34#Vp$Z{l-1m!y+64JQC~B{%yP7H**%W#KYX++J%lYh@HmRs zC=whH_U4KfOj`?Z5}|sssdTMWno~z6tFXmrbO>2ZjK@`1Xx6YC?>2;SIuxMkLOd(x z3-crjFtYRAbYU^lP%5HXLqE+Ld|u2;(5zu%9EMa%t(h#w-c`ZQmr4bU%+FhefHK$@ z#UkQ34LrYg{~o11j{4q2{RR|(pmd^m9IyI?M<+AZOYAv6~(PxfLd?nG>^YU`Z z0h`j>pIyjBX;`Z6dTLC4=g}#zE2AyXxola4$^7P(F{@TRs-8PC>+$R|a$bs#bLFbK zX47CK3G~o#dXs1hh^xo2ZQ&slP2>q5&M6A8t77XJnH4Rw>pj;XIWNK_ToBhLvj)FK zSSu6vFIY5#YXz}}B9Eda68$BRAet!tIA6bh?G=yhdf}HMRy+@R2wAxASa~=!%f?@? zSgL`^H7Lup?(SGDmyJ6$2i_L^?LUAEMYxI;r)Ea%<0r;m-rw6pLwPz4l>l^L&zYQ>He7 zR3c}B_~RX&PQCdX|Gf9jxuh-nFA6*bSjk_%sUJLJQ0+cihVumONk#$atH);?b<5Ts zb;b2*H3#v@@FUTI!-wAf7Y>ZuP?BNe3>gO5qJAl2-i0{2leq*=w(_vJr~*HVOyrYv zYidEQwkZ`=Juqe!1v#K^s$oktGm#I08$)-F7WyxmTo6%2pzbR0pZyin=X3y@15{2$6h_UQKydR5ZsBb07EG| zIrWpnW!0b5-Wn(b?VNDbt7qVGw2}v7BfrrVVdJ#tM6GBU%!h1JqN!-3+!YK&1b*UW z$|oDQfsr`iLgif1lQdizM`TMPGJ0y`I(Qs~#Ml5wkE6C4c^;V%=y8LDsjh-03%k&NQ_=6I#KgBCnwN{Ex&q3?!Z zqHQR3gIEqSjR-G8iHMa$hAMai=Z-7jTQAPlf24QAv^0YsWgE`u2A~iZCWU7apa7J& zw1JX&C!QM~N0CG(!XV5`94$~0rP+x%+d`UlHUTKWnvP(2)sDD)4LjvHoKq$&SD*<* zeOg!oX$ZCIjOvD2!)7^zf}RQ)OVQBPhftt3WQcGHrfI|EFoYC%F(@E?UgU#j=wiCS z_=_y)Au3a}G-gj`KQO64fg%ydsEk1Ao|zJlqx~!3adZ=w;L6bV`4S4b2pQ00PeFA= z6(Ax=NqQkBb7DHlj*%~Eyk|Po>H)CusIf6{VUunns3^D`Ehp%pkAZgRFo!}7wi?A; zaHWfb5>YXyh|_@TdYA@+p^d`fRt~=^OfO<6a~$`aGIGw@s;6X}^bLTuaR|lSTtOX~ zf>{GlC7x_7H$SJanX)U_gZZmZIi!Ulq`-?p0Wn?3!E`}H{^lysSv?!N5b3lRPhVt9 zDX^r|31tuP5Q;_+kDu_?;8k%5mlm*84FXD)DkJcOAj9JF9mh3=8L}QDdWgUyG-Vw9 z>1n1z365!6SVCFw0!m@XAX1$N9whb8PL4b2(G{u+69YQ!W1w{7fkh_Ow0g^i0;X`7 zXOi8aFwZ3%z?`%Ohw*ROXse`n97SbFys8*gE4#2>2o;yq#Xk(fk@T(k>w<$7Xx89c zx-*^(ix|LKLb9<~7(xoXI22&#BvZQhOcxfTCwU1O&&+gD9Xc{0tAb|u((*m+duvE` zX&1=?i^J6nR&!;7W#jz%xa&r=13#`jRscI<$5qq?-Er@N=dex>bHPfa3UjI?+)sfQ z@GuIWe+WB@)3Z&gX8+Nj{7E#;Ic2aS)(#e#?MmT%LUKJk`Z)2r18fB{6&l7uIwb}N zIM42RR&DBgA0)Nwp(Ue6%&!r-S_6qfZw)lfGEgYu(L6%KY%LK!hZI;&3b5B9pmSfW zrVE+??f*8JHTVOR(hwHav^3(oNS-&s8xh`6mlJv81~-yT>n%M@f^en=(nSbBqw1Gd zSHWL2?;$_9EDs$v4kR4eTTtZ|kE1Dg9NqWa&-(102an&5RES9A6rxeX6*?$7aX$_l z2F>UEw`({M^YJ))!vk^m(n~G(>Boz(j(9g_lE0v;)SnxVqtY*$nWAt=!y_1YT~v$IoDl;=jw7E2IT4$GZTtxik1d#Js z9T+9i^%y@Pyo3}8DR5>eP#=tB-&&d?Q_|xy$kO%DM!e=(vP?)60YG~kjSsF|`!z)T z@xT4c!^?*IvU?o;+E@Rfci+B!JlQ$maTNX;6A5+n-~oCZjg(5WUL+2)2=#Iyksc2u

k#S*y3am~<1DRtk@o$+1Uw)tv$#+-Usc8{Z<{p89xbwX=u-{PGAII_}*%fWOXoITRHxk1T zQs7i6Ad5Kcg24dk!NnK~J6Mn?bxDfnm1gC@39YV?@d<_%$5y-u%o+|qE{9OKpZ)Zw z3Ozl&w&OYu#z;s9(S{Lh^6){uv{hNELxp@534wVN%yiD8M0ngU=*UIL~I*vPEEn}!a6A7vGh-q!?Vw3=ZX0sZcUQ6NJE6gjO0$b)Hw5h&=jmsM(b(S00Z>Ve$Z?7;g=1jdUZJ zHH3#ybnyDaEK8sOK86^Dq3OaBNQSJ7nKfWAD=@2$m=O2@$9hM3JHc?-7#0wN%E@vb zfBNt{=c23rrd*BBpk3Ss#Hnp98ja*LYVtck>Q^kQ$8Ufr*m190guK2=VcFW(2@S$L z7c>P-U&1F*zKSgp)&<@6(`X#X#>KCN%o@@;WzEefwRi9F>k^q1+{cI(UWg0p1kZA? zS-D)<37#;%oJ>nxByZcMtebD1&oOyA&+u@YMl)_0iS@A4y5}CTQE(o4>SwRI^!n9m ztbiSbu-0c5E;b~wUNAX!;#GL;Dyv92L2d^1985NlN3w~!j%M=anPtNeQXr(j8Ki(7 ziZTSLsNrzb)5Hc2jLYV_%5yVQ-9I}t_r1)D71=UoQT|35_<~oezelNO9{tx3qtu__ z$Hg}}mypty2r-CgCwWO4Dqyw9QV$f85iJKCE?s*+T!&M?x&3x}^#W#At;DKeSzp7h z8|@Yjr2~ghRLR*OJ@xkfRI+UY>xNrbfJ_0k7J@ukC{@HBD{6~}LMDYFq`=}7Sip3l`M`+9L4>Id z3UCh#o$3X3$`&@R$TNnqU4y3sunOdHFihd0&x~@-b|A2*Ofss~^7q_8Bl0#u*g;D? z;@U+c*DzR!{lbxBA6b9-`dASW!ma$`l;OtbQtFFOl+-0XQ5?yIeMA6sqU5Px*%VXd za#4Nr*-5o3V~d9dB6z$|Rsa6_l*+FlkMjAD&#{SvVXQH(+CZAB)S$V;%8ka4t|qiU zEOK(e+Ar&)6kb9KEIg5_zrh?&8TLd~#fGc{YN z08mc^r)ESdki>u$anX&zbA;+Dgx=l(!7aolSM7F{o|3E5)Vw?(c zUiGs^4&tK3GmTd1x(pR>eHn1*7^aDcW8e%p3BS%W;GhgCRl^iAss`X&#||9xMeFu7 z{9%!KNgwmeOQG;jNP)#D0O>q5U094jouzTyQO8t+!>ftuQr&o)V;;hhO2M8znMbDV zAUM{DET~m6;iDG@<{=lLN(n*>HWE}_yIOFqS^_TB5ebG61Ket<00uUrB2~<=Fg4?B z9`3Y|=73Sg)l4bX7z_4V`kznU;Q=EW%y3Jws|(@=V0b}6U43kSg; z&I?V@ihTXVSn>}FH*JFFAE0B~T5se7VhI%B#f5^6maWrO7boUK;Bk~99j1ghJ*S5I zD$=IFM!S43qctEoiM-srT!A*M3>cc@Hmb)=Q68v)BS9dW;isi8W~MckF4G`VqjY!| zQXr(j8KVHkFPTT(4$H5Y9D*U)jWdxCOsQ12rFtNsR7wz$Wbm?-rTx4!3VCIAhAWfJ zOpGv08hIEwLx4Bpi8UEM(73l#sg#q2jZ_>)Q_O)t-5RE5;G)^Ix_U)b4d$aztm`RW zSIM>dy5e57l2O+TII0^{J#tO19&{yZDw|4jh6rs~qdHcuwPUPU2f9El!wDFV>|q1h z7;epp!&S4y7+g((@ETH}Ed|(7!p_o`G^Y}2NQtXf1yQb&5h`Pqf`+EISS*SDsREg! z0)d79xZH;Sm=DsTz*D18X7@vosX)M?4r>ybc~71AEaDT8;;0Iuz%x;SHweUQfr-d# zenE!DI8GhyAARS6=-@MJRTUFaB4}6&SN&!t%W6xO$H^n!=RmUGW&+EaUP;~11(`|8 zh*^UwdPUU}1E-@$iiVpjA)A3K4>foi%*Nqr89Tr05*CmGR!QivxMhdRKN6>@oD>fK zT(lHm&u!Nu*ptJsJQVN;oD0S=2WAt)%9$C9bdel^`IVPOCV!4b_GN?<-kWa+M{ajE z>HXqR-s?)+P+^frSZZad2D7M)SA~n@D@)2s<3t_4RxmR!7k5-X6Sq=v6ea^CU@+m{584`+onnPBt&*hGfisplHd4Gt{ajm5C=* zcMcO>-@OdunI4ZJ4u-Mhs9^{x5Tt;ZCzxK`lmLb$@nNS4lI2wHAtT5*%;b`4!|HAk z8Vno?6mcw+MQ}F#3>5Arw4*v_YSH3eD{N#kPw&XV{Ym@Yd+)tQZP;MyZoclrX{-D3r!}lc#Qx^U zEpNO!@}?VZnA^PhiqB(;`BpfW2P?)RpwFiuIKgcNq%Q`vyX@hGz1HPmrSAR38pU;p)u-5|w-qO*aoocB&L01!=-i3mh&=MnMV8{&*1Sh&`3ZgO<}`b(ZerXMXQuH=(GTRd&B)Y{kHbLWQ>>hK-7tH5r8lTb95 z?3uVOLn% zq*((@9q>;xv3VfEh>MZ}{GyrPMM;1&*JvYR;JT1mgP$e7zF%Z1kNbEQ50=%qbWp^aIC1!HlsyG+0j26nEz>9*l?a_A*(r+cH@ce&fQ54TM_ z)#ff6!vcc=L$=)6vUl~Nqm_5SB)^2NMNyI6jPt%sSK51Q?O@{boa|Bw-y3O|H3%C9 zlM2lmPW4q@809=0&XuV9ocGy7gwctHOzTB^*__!>Sjc4)YSR#QI0vRWv{Z^=^RorB z1~4o6a(w-sVMY0=zYdS%YusP7Fe`21D12^$I;ns$WjB z!Ki6ZgoWbU=MG~}fdZV_a7C~LJ1O3qK+^>m$GX!LsVA+R39zc~?>O|oQu(1l*Q-_V zYV?-|f(uT=%8VR*;!`O9O(V=0IHz-kos_ssI2{DhXE~{}xAv9|+qzkVh1dE3)t-JOS|A(oF=BRNqC?O z7Vj%om(~8mYsO2_-_PXX8VW`M#zitcHwQloT{iZ&Q zEAN}HTb06pwH?1;*s9d%Hhjy#{Z>yF3M+7f)8jQetzdl*!NPVOt5Shd!8iDnSZr;E zkv!1HwE_W(hUo|>8ga`rvybjN(#dRL_cpJjH}UF+T$TTjZ09vqwgn?!f#jpb6K zhG#5r42T1ZD%?YaUV6D`Uw?YuYtKd;b`?G=abR&vUkqe6YA=s2dKtM-hPPD*i(utl zoBF~@gmK^ zLM}xWgtYV&eB$L#uQf>c)B~^i@@jw3TI+mp)$ry=o;z_1w@9~znp_Rd<Kn-{BAs%N9)lhZeLcXV>vIui5I zl^P0ZL;u2~&d|dv@`H1x?&x?}% z&wCL}ouI~*ztyRgzk@XTewDZYHZJLTz*$+y|Dxy1_328R^Z&N~TuHlwKJ$G^vj#WN zMB<3=a)E9~>Tq8Z#mxI2{^qgwW#!RDL=D790lC=D_1*Z(i`kJeE}?-BbNB`#X?me# zAH1W(GD8U~LG*-Uk|2o{#a|#EWsdDVeJ%>(a9o@GQpw4@+Uw>RAM$^-IvL`8UkEo4 zJ`(GJsf**>-Gf|QOGDbRh83cb73`Wi-sp8ZA$c8UChbyrJgZ!VS_y9rJqg^_#G$dA z=!=B3z?n7p-Wr_hly^ZMKj#&EszT?Rc|}?k!h3BNie73|kfasoqLiRjX_NkYAZj&H zz>Y#5%c}RHP*DM`w_RU6~eBwYKq_fOU~C*>jJ-M`o+L02c{Z!&#nYWEq{^6(I#|U zsaBUB&yAtKm|J5MRPM%@4-GcB#3fYWYOwx8_6MsLc1Hcu3sKOGHa+*;O2HsJwehFcT-atU-nffRX zeTyWR#C&@-78CC@?#Xi0qJ2DIvs)?E?QF4xTW1LuJJtFJ}+I;YgjLF4a`=klwiCyhx6=bFm_+o>!YT@qKr z5;fD{9{FXVy(@-tIr$}?+`J%hChL1^a1*7K&Y3kNbw~ZY70&qH8dSTQE>t>dv_-UaOwu>>3EOC*lo=B3*kGBTN#8wvJGQQGBYXMh3G+~SP?%`k%ex&K8t|3#|9=myJ zA!F54#Trr6wXS~@$ae#3rfsg%>am&?(u)>tM6JTfqD_S@Ht>zB%{vmd*qXTQYQ*iS zgU3<%vhxV&SeiBaDmMMAU$#aY78~`L6G}$GK8Y^>WFHLd1+gH^d?LGeJ{w4FO_|C^+R1B`5sD*K!=s{C9tR`GKMt-%vEdq|}ZX zhUI{8@B^vkpUb0hW9&}7T=50#=~%=WD3AsdvCaY+Ma3z37IJvkz%yWV3`QL_7EolZ z5}hrU8m1sBSyW0IDl5hW3q{!+clf)A74BO za#jIkZ zOifyi#ZkiBY7-}BnY}mJo?n02W_KL7w_f*78;{J`M7d%^=_JS&1ac&5rC&N~6Ce0f zSAWo6_3_wfhc(-0%!B6--m9suk5iZ@i$!f0aQx}@6nUOni+9yiV>dSHD*V)lnQEZgk~a zKOOUCe*^mJQ?s`4rb)Z!)_YJ!ceC4)iMY@#w=rEmW79wL`&P>zSndn$DeF!;QIXj8 zxjNNG)vY*)w9o<+TpyEB_4E29AII1Jv1RA}nT3CKz&705XJ-t$>&WLf0|u&+oMOH*%KW+-Z00TxWX@OxPSkp@cTs73Bowt^5w+W!7*G zgf74CdZ)3LkE(Q~X+0qQXcw1IjBp7BR8j+Cq~RR;k%*x7sV#z$4h@U20bRIn7!W~{ z^5lEUCwi1eWQDs4hssnRmIO+rDF0c~dgey7G(Xp97&R0gS&S*=wI1U=*lk!sGGx>H+%1qpA0xM&r&p_gQJYt&}g zSbdBZ(jJ1=pz(HZp@nh$4QI-?QZ+}W8bm2zd3H#u3u|k-FwIeVF5%?i1ly-TWd!l zLs@Du@KZUFMh+P)UQhfChVHc*V{MuoE7ukT5UYMO3->vK*TIxLLt^43n!MhGizutKm;d7 zQFr-U(L#sF>&J}A+c*L zmVkh2lAq}jjmHy>=)e7ye-mj>Gbx4ET0s>H2r+CMByZ8L<>q*{Pv{c1#}(MhCYjX8 z$O+rf@e_1%2QE-Gn#${k<)`3c9LRKU>Yzj_Ao*L}+RqHyvD1fa!@656jf}^a zXQ+)-?2x0%&?Hx%bcnkFHQHdXBm?A~NdFgMQ-1e^m4+U(#MrgecZ&JV0%L+VVzZWd z;#Nz1I%^}hjoW?e?y}j%svVn~x7Dag>L9f66g>ai7pXTSZwM~mTM88}dTSQ1?h!&Rm2rO!tE%&mZudiC~(}Q;C^g-Ks^{{o}YHim$`X6mge0%)D1=`+h4CM%zR_Q`YWFda zIx!KD1Y@Qd<&ZNhf$~Dy@vT)(KUA>V!8uek;G|n*7|=4gK9Z3QrI{VK>gIy=A8xmU z+=;nrP1*XG`z5Y(iWQ&~*3N1eeEc5MdfU9EAKhS&A39*~>0f8-x^s4Fl6UZG zE0+3FsTVy^y`Z{$35A2O4`=tO&CoStIje)o(ob)5Uo`wnyHepKEU?y!)^-h6vkx}1 z;Vs%=s_v{K)E+4$?pTFLD9k;mND#2=@$98eJj=(D}@oJ6iu>LK4?AC zvAAG4BHCWqL7JPYR(jJO<}_3V>7hZ$hqp6WgLq-Br9Z#gn%8QA!R zG~g^I*G&I|XC^N7`mj=8`~>QY`l<5$4M6>X)pq&x>Gxk#()FK#a^xRP6w1*NDIs04 z0}Z6qDs?%i+*W>9e}vspPPoNu`>8On)QOMK<;N!@jA z*Gm`p7KPN}ID{Z{8#}sMp17=)XZ2Cd)L%q02GV;_4Qh{|ulH)Fik6l)LfIK87HQL4 zk^VRGDKA+fEkBnwSaNGjTtbzliRK3%PAXMn8Z=ySevOFR-BT?E!bh$YMZqz{g~#DI z)V14ug4X%vUG%vE1e!bk{PD|f%__1ek-upv=DZ;61LXll6dF1DRs=P zS1U!ARHC3F%BxUl(Go(Qepeh07B%`~A+=w{JKYrHAtB^Y^4BKLu zL!?kUc(6t`1%>ix*{1&(N@&|*d*{0ExA{WdPRuXZFw~Wk?Wn#WIFPB2b^0jsoC@i0 zleRbMkLMwf2FprW$T=_K=^uTBjw6q#Ehq+_{XSM{d&0OCIv8{Uxh)EwH1us5ZsBzUiAf z$&1Hb;50*V!3>ti04N=)36}!lHhU;T%)yqA-UOF1zzipSHUw_Ir4-un_v0TW<}2>QkSpeD&|X)IBwh_NrJ+ zI`agI5jB;-kLu}@CnP2fmuF_ZbVuCUGTQH!5tamPVlBn4NuKpR2lYhb$=nasQXql^ zyC{=Wi;F*o4@feAu`G&$1uvHP>RV9l-9u-FRr?Nt&<{b8vKx(G9mKduy_K+}5>PZvkS%PG%8J3F zN_OKkMQjA==oU*qu-YmeJ=W3DVe@0I6I4jxQSz~{Ga%3T457?Y>~;z_DaFwQu?e*DAo*j(8QoZtz)9DR;>53;?c296{@{ZTuGo?E_O*qmJ@UvKQqZTK zeb%-1)4%uI&wtu%1I=MuVW#*&a1z)Hx7>0|xfG6vbqU43$&w|tixMHg)aa=$p%{2A zmr$q>iDk-Ri_WvdVtkflcj-!WGLOSH7dvOWX5V3(pSgtvEsaUqPR&i&L~$Af1Cn4O zj=tp|vEZL$k@-HkL`_v#=-u_TcIPekTliWQb0%)5#?W5XPBNkWnIzWBkpi&2VJ4{- zh^QJ;+uDJR7E5Dqge>gbt*5cXeFBWV89SgD3ltfzaG z9Xfv24kEkSoX`3!8r^e%VqsYoret9ib0o`zjaX-*%hkIOo3!nvJM5~jzu9UD5CTZ} zTw&gh&7Q$T0mT3YuH*MSW{c&MsQl9`EVe?Zsyec-90K{fci(T(4GH802y_=}HuKVP z>y`{~HDZmak2vhFXeK@mfp=^ooRGGcRan6?O}C3OVPZ3z%av;$aFEz!)`&L`iF zf+68p${L9KB?Lh>hR&xJ`FGPkfd-Vawh4ike0-w01Xe!E=28GsT8)lR1@er^k9GF{jvU5jl}mbTWN zY;EV$@keAceZz0(H&-F9oF zN|wlW+Ma!o_imMmJ5a@!4{BQ-SyQt(C!Vk!cfT_s z>MI8|6%~0aXb|F1VzT{dbv$_O;$HPbMWB9{fnlw40=pPkIM}Tj*rO1G+ep;xpJvHd>{V_r+335T=b+OVqmEpI1r=do$_{DNJ8Ki)OHWY}WCU zXyOp{l$s~3QpkcVPeAO^XgH0Y7dG10zy1~5bK)Vp|NXycFP@yTwHXBMD>*L{m;eAk z07*naRM{5wQC9Uy`q*`vMUi7c)aOn^M)St4 zzFr1{L>62uF=aEMS({*HD#+mV^GE;2?3a5%#*_B0@B2X;9k1GH+LJ&6;8<&i@tE+M z&HCpLx7DE#q)5;}YZ0{}(+IO=JHmxDvTduiAArj0wlg7Yu~enD#d^wK`Ya|Ie-*Rd z+f#Pk&3DOs4PklZ02%`-NjLwljTz6gaiv}iwY+q*yx zF`I8<%v+;g;MzbC9EqK^VrUYEhxU?9bi$Ls{mFm9kKsAH;VnBX)pfOv9Pq3g*=8Nq zX%aJ)TH?u;D8Qe(0+IH#zZv1kNHH@E^3V)qbz!_-zowu<|7QkGRUKq>a1ndJzPD7rHr|!m%B6@aeb8jq{HGkL z^uw~GhCOg6E)82LWFfO)wVCbnR_SJvvcMBKOAN~pv;)IZv~>-3>kur53c@ELWnn0Y zzPYHKi^1wZQ4loQwOk+66_TnOYAFFpE(*kJvI`ci3VjG5yh}H4u!^?re2gD)ZL(+LIU;hn4PT5U?YZwb~a?& zC9CsyOdm-=J*sN8I~Hn#vf~$ZH=C1KZ+BZ_U6(CFEycNOGS1Pf428z9(ea%5h%zd; z68uN~5+W0fUGNne+M<^*n#?`aPbl74xCdfHY^vbeZaL1k*54Yj948S%*ac;WVL72z zShY|=AXl3Np?Z4a*0X`ZC5l&J~pm2GSM>m!`>>_M%NG4Y?IBd3yLneqswLn*r3^@ zJtv1!TL@|so1zFiquo_BjU^8?Sp@14P32Ri7tFqnK<6#9_LkLLkB3%sIXM8h8-;)a{2W+QCe+3@&VjOA*qj7t-cH!}u(R zrVaqnB6-b0GtV!WGBx{z4*en0ynsQ0Yh=qq))bLoS-YDk7FoH8b#ebBJlBSx9LlSm zkhui=lxDD`@o{zV&V``4s?VkO>r{av<0~Z={;rZO+;IRws8~;xt+dpDus(vU&AzbJ zyg`g+VH10L*V>E6MopT*^JG^pN9!Y~Q{_;v6D+Gdo|u}J#6sNl5zHiZ#e;>^>eSFg zCfZpSTad9OY*V}s#Dk1XyE7Kz?cLig^k$CbK#e65K6R&Czb_1uUHLumdr}3tv87vK zEoKD_yVj$ir`JmfFD&GusG!6Z5vtn#1$|Vh5{SbdQFa}?-=u$T?7&bC|B)gxx*E3a z{oLddx6O$*>xp4Z%Rg?8bqZz$D4aaP|1DSux0Z9ZX?0KX_L93xnd^pZW;J%sA?&U@Szc1I7B*KNo~NptjO~ zasyP)4NkZpMdsMZ%Cy3-sGK4V%?^RYUpO*@Jc&&M*_R*d7vuV-J~|U@$B9McW>Ukstp^pHqBoJtp~{$rYEP~ z!OynEQlrw>!@K`rGe32=dT_2aN!3OipO6V;dL=keQV&Us?on_ zrMK>B<$$Hvd;M|TMl!egzuC@@Dc9%j-Zl34V|$!c`(>%CR!ROB6}YxCb6oGVL}+^- zFonI)88~-n8MeivpCWpF!8z$@!{}H6DsB;KlOGh-#IRM98>3$|Jn{%D1VKLHgz$JG zBSLyKSyGUw8;+`}&spkh$`*z&ywq*MWJFd!2J!!b++~9);U7mN@jahd;^BUwPyahq?Vlqy(=cye1dPD-l&t_a`1?r4U#! z1syj5SIG`bXaGEbw5lGCggx}N1X&J6x%FAz8uUxxP*1g3bP0u1skZ>f>njf`MEUrW zOh<@H@*TAXXBqg~LN#B4C43lJ!aI@wz;=c9P#?!(ynC@hsZp=&e?qr@R)CQyUJr)#4erb9hfMDN@%-a4WJEt z1ORf#?^|!F=ZEavyUro&>a(LKM{R_IXxh)^%i?)m(_ZJaXSldY6kX~o=OHKRjz#R>w4< zqj{C}zh$eHIs81(KWOs^>6e5a6c#Q3>%|_hN{_Wi*F#NV=ApceMH^0SvFry|S+%oh zX{e`0bAB1Ys-0PoKqu<}3xAX8$Wj1au)Ps9p9(RJ7wJ7V+|WTNcL3(FK=>3gNMu?n zOC2K=8{=ks(S}ARY++*w!&U!%3oQPROMD9DXnyDNDci7?v0Q=dw6VuHs%6zVJ2x_? zQO(R38||Dmw^{|$SemZt9kK?V)AI`*s^aGlJ*(HPmg$Yc}(jiCMP3~^YX<~Jj@HQs?8QQbxtUdE5>3_E_rvZ&aj6T7UO z#tsIGV^exHi!#TKSH(?8&Nkn5r-g1}v86F<7@xJ7!>4T(NVf@38(C;$7mIQ33k^zU zopm5&ts~5iar`B+E5=ThMzGJhFf@9y(Gs;H(}b<#_gd;!EZ#ZGRz}A0kI%hmYuiBt zmqS(>V+nbgz{%0wOnmISDDT?*HV_R63EQu1uFt%Gl9As|d@+OtQW(y&B z1vyEmg0;}yDXYD2)^P+!y(jHl=^SJ2^oy3B>9#@#=lUp22usqJp3xnad1y8AH7+w) z*I`q$P1|#9(%La})G!7iTwo(etOc$S>qxD__NogC1x+P3W7l7Ihs8Ig@MaOQQxmw~ zsgAk1b0PcV7I;wLitqE5i+*UPf`r25H6Y~-!sP^ZS3_*J5i4+JaX7KbuD*3QaI9jw zk+m0Jo`B+EELvp>4&ibSY+c6ENnnrMOlz`$E0!&N*ILh;F>6Ryp;~iRerd+BvM#IY zrjU@%7xuEp-w5T+7^&UjG$dn`tn<9U{0Z%j0WXvTv+u;%H^qF8*(&OlhmI;nwZmOE zTGwxHvL>b`v2>d~`6396iuBJ>l;;(A{tppKRkWCM91NE|qK{mB4k%uQO^r6Np=7B+ zpu;AzO`|r5NWDTv`a%v#h$GpIv`c2se&qU=1Z`zK0pwP`^Phbzaq)ZJc+sP0$Npg5 z<~0!rY&T{~rb(meI*EIfzkIG@YcZ48c{p&BP2+L&u8lb0DwpkRhh{m8g*qL~E3$r~ zShx4?=&?;G!hrd5Unxt?TWSc~g3KB+Eclp6X$MAn3Geuty)_7~^9%;slhOb-IF%lo zhRS&?v{&QroXFx6~dnWW8tP+mLl2ewM zxyr`BQnz9hj|qMK*50+5&XEy7$qh=i?-;DfX^UYr__RH@3_x*eqA6h?TqSCwsuj`p z^d|e}zre8X+EKgx+PiQ+vyO#cwt);D8J0*5(ya`t6SbFoXq5(@WpZ|*dT-j}AC_r4+MeLv$1Qewdlg(L2C}2 zbrZmWjCZG3F`Hx8Tl%9nTdF%^Z@%SryJ2&$J^J#v_2d4-Q7$B|cb~9o*FSN9I)UK^ zy-}TNIN#s?8a;XnguOOtZ@%uW9MT41Uk+|+)GGzGpQYuYuCH~TWZVl(rAeDoed-f? z%t-CDee>fi`i--;`?{T&i(o7(>QUwL(dfeZzk5G(r^C)L3znL~LK-t~RVu{c>Y^vUN*5)4gAIzi2C=W`CjWWXF_|*I`<|x=uxrziq0-_6z zcW!KlRiq)*@g7#}Im8S?SQXBsCO8D&IGwRN7X7(ykqWK6ehHWT)R-w$aL+P5i}{2W zeJi+2C>ZaAyfwfPSYez(DOg3*4G3EY5c-ZgV@%!J);#d3|Nc+g6R=Hk6v||<8X|+i zo_x5D+mz|~MpOHuC{a=3B{uC{TQlL+8`?a;({k9Zc5G;gu40-b&ID?1)OLXP@0_K~0VMUV^d-c>D!gLjTx`;3`!%v}>si zj*ndu&5GD49Zrs;?yr;)bjsI}%PT=83ns3^wT#er`|YWR4uV|H*|k@H)Xq*LvjM=a z?WB_$To%qA-WoEzcYdfG>mrj1Y5UkUH3?aXZo?`!Ec@UN3va?&ynon+S6__)c+9%5 zbkV7gE@-4BZxhx~A7NXY^q~lA0!q$cSua7gWHAX?w<=cZlC{KAbF47OoI1nCeXGA} z_7hiI;>PXv)Y)h4fxA9rH>_W6j~_mP;V1%Y)|<4Au3EHcTp2RT#XtZIL^WW5isX84?#1W8o-L5BTRnFT9s*q1NV|0g*rdr8UiP4 zQ>tHo^yTCNTk#)&)UThg-M8J%CJn+Q^3|v-61m{a8aOQ>Lc|aBh{2HJ!9R@ch$k1N zwg!Lh1=E~Y-tme0@aJBb8D5RjqXsdebChF<0N!^k1W%*W9zWTzPE?}WpsVq`EhKWZm1iq1ZcDRSOTjaNT?2aE^Ys0HHn=G>TpB=Y0 zkPGr$6-}TfH`{$iNM%U65N5(FTIU7PY4MUp9-Fb$)3;dUKW6Oobu;$%wYTDtp=PHz zGuW$RIN$Ti3r0)AW1#~2AoFsLa!NevM5I9sW4wIJzS9ga~cQ)Uit6v}o-= zfaUtWRkru=v$p*zs6My2=$j8nb(FxACm$%Cr#inuP>_)gouhN)k6@u*YphwY95?Zt z=K~c&Aj)|$;IXo1+H#>@TYv>#n4PqIwjFPv95<1JHU(h-6-9mi8A%~#41r;_#g0|U zC#w8P7om!L#h|(ft~|z52aVJZwcMFCurteq!zpQ$@KS-cIaf2M zO{-6e7m}QYBD(8{)j?L#@&={|$cgb%D>PrC^c5crN=*>(^d$;Q8xG=wCYuxQq(s4T zi)SCfW+Ppex`yLU{b=++u$acVpf0M2R}HG3wl14B__>15dfgu*Db%phc7Ls9%$V>S z-GYbpd!kWpBB&3^N?ycTf`>*$=QuG|Mjp&Y=;J>uwG-GlmWzo{p_1{Wfei3RQ{$Y} zq>wWn&GIV$oh_D!VJUb$D(WQ)Pg36Lr zx9pq*sHMDD(6(1jqLPC}?WgqH?SEc6Zz}~KtD!*IprJv_z*N+PT>Bsx&)d&3+ z6&mKKQVmv9Y;B>&fp1Js($MRoK>b}d$m3H;qIUeUuxS(J=HG=i&FAMDU9IzP@& zt!VLB`lYcJE0AO+3pL2)cB=#>x&RicUw@=sfQK9Je31Q0Ry*@FKT3fXnqk!nDQzG-F%UHU;~>q$sa zae5Er+_QcOiaz?rb{+%Srp=?36ul>bygvd##VY3#!IjT@RYkcz0im4gQLs`uznYkH zXYcYD&~`y}R$;09KGQmqqqkvv(4Aw2;OeD3*JdHXR<-Us_`y4(T)pQfaP9D9p9Tty zM4h!cFcEu$HqRBM3MEXInL(8w+QsB;P>5f?nZ=Ikopv}0h^4JiWA)pqLm|ZJs0W;*c#!WE6PPIrms%Mp@{>jilq!|5F_Yv+FZg|3Czua%RKg-$Pj%V$r9q+(e z9O0?}e_La48Q7!fv6bL*6U4Him=bahZV%sb!xi2A4nsi1t?tr74F z@o+pws(zK}HV*X#%ED2QN)EeHHGF5O4M98bfMFi&NAO$emVyL!qXOx2Xg!zEmf3!o z8N8L&m=jA$f^sy~^%82w`7F~97PXEPiS4Z5u1DuRU0BMzT+J0fDKAxN%BX5b70Psj zo@&AB9gbY2g(|e_ z4u1IBZC+|lvEUMnn=?hB70SZd1inRKF=B`{5WKmpt}Ucjo$xo7JdSh79CpRIOjE>=K7x`aZnRQ{4#18b(OtIf`gAm42dS=VY1mW~v-wWddvkW80ZJRg( zL)&X;T0=6tL=hoQvw>xPSM(bGP*Si+rA3Rzpw8rEFX&^dShY2dHnCRUK^+00d8Fbx zYMze7cFX_3I%H9U_`C14xlyGds9ijUxJ?+@GdL2h9dH$;f_)6$0q8dfHkuX5FML$H zgw$l#unw~ZM@pznf{>WZ8bVdHCA;#dOxcZ9u*l^;G)QeRxnJRxB!(-3 zU%CGx8f}?1oWngWy3DN&HmRxEw?00uvGF4eVa3I2+67 z2JFU9;tw1bEAcGeA}8_OPJM!htjA*$L&zUGvDIqW&g>Saey=n06SXWZX1EZcG{GWc z*14geUph3s7Biw>zSokod5dvrN0}o+550Ws(tQiws*f@NEz!rRRm>pD2iTj^)Q_OI zgj<*O&6{lF=ndB7uy+IsYYg{|bG3O`byg%=)YF5{askLF$OpH3sKS-6ju+N6xAfd} z`wv-kJN-seS3utQox@|6&#$#Q_D+ef+-W_J-O4F`&)QQ->utx6yW8nub)a-=C@$z= z&{8G@yH){-+HU-R^*+yxmNA!BnWFGwfQyn%ZQE`5j_n|Dumtnwz$s1x2&k=*k8oJs zLf0`j7!{>MpdFZ6CbB z`tRN7%o-Artd;j2aNL9IChN(0YnViSg;_)%Zw+bO0Er8zfSj)??za3CiXu=S;vPFw z5%s3|3okIk*;iyiLX1)7s~ia?5A|5+?>K(-o-nq3omj`G?Md3&jh)-8A*)tb93GHa z!{WRg?FBvb5mubJn?lPj=MV~tZJ{b37QE0j)#`1_roEen^46JVj={=^!t3E2XAh=3 zH8`D3xKz)^Lrx?pjcJPe4D#8OCw5XLh~iQpC#F>vN+jc6^k+W&!-xFWm-%G;mMpZ? z5)}p|??N}9ExA-%-x`}cbh7#H{^(BzuM6FVMU+F4ewBi#k9J!bh2T)jqJg^DTLZf) zKvEVT0!vw5`9U>7Xt)#ixk=6iB(M`|+Bp!12C|SmW!W)Q_dmYM${PySH?Ri8J8UNp zowaNQA@21BoBExz*1$Cfl5yIl{T!Bx1EwXkipS2NjhR4z*_lD8-Qg^Vwc|LmhW?XW zL~#uDdz(FLFEF7H*s_3=Wo)^=dbLgd;8!_(EX>>*0s7a(68%8-h_(Ia4Yn6e(y#u= zFWConZMVOE`gz*|GLTl~qM+i-~bAEf}=KKVOa61GYHB zYw(-C-k$!>A?wjWYSht_e=%pJ+>m^w`mdM0F(|8WF`)}Q$?n%N49XCCc2 zcf#x=*Wk%w!fwCqUZ`spd@%47SMSBWa5uj1F~&R{0_m|xJuExJ#UUGe8rRkP&=L+L zZS$5l;XMOkGvndH5(Vo2T=^*#Q$X!%4|iGAbmp;_0x96-BEkg`i{VL|l{T)`*6-je z>xuusMl~>JH(j&c675@@rr6cUM>B`p>O2Uy-i3Fr=Up}j94OttKK@0Ed}7$@*UZ|7 z?zj&jc-$7`B3Tn0JnyAo^s;FDu>Gn;*)Scl3)_5r)V%M$#ll~~hO2`s=7!eW)NyV^ zLLx2MpL0gl!a}}*E-oZx0mb>jG8@ae6E!H-iDys$c^9$oBPP{r2>M6c#xp0QD5g=hys9^de6g$1|#->Xi$(3Jo zX^=MWeG+dYFSGGbm)XaD{*)DnKE^?*T{8`TG!iIxgW1s^#}3w-IeDy6=Z2z zD3=S0Vos`$VLVAlljZjj1dFxv3yl};g{NVIZydEdI_|cfzU_8=4HsTQan(Vhvv@hJ zMDPKHmQ?;4fj>@%>N_4XNAp|-0tLB?bG6~2Pe+)#fbiurJC9AD^fr(2D&8MSGpd{t3Ash#4oF7(zQyDTkj>{whmZqueNcka}! z1GNGA9fEImcI*p(@|(Z-8zgM!FIX+FT>s@`)v{Xjfd?Ld9)IPq@4@rqSN;s*9(?c< zE>U@5zFL|paw%s?xZm%-c!$M!xHvoM8jYEpBX6Z~@pYO^ZgTt9rY(q_U*(nkT9MTk zUP_w8$_8&X*$wh?5n}jONQ?*`Wu_%ABuNQK9ur?$ZPl&M`MV;M*yW|}EH~+d-*e8wA78~C za7FCfrtMAZhwag$Bd>n=S%pxR7D+rL^x^k07s_Ls)NRL8Eb%BugOmmqH_jgh{qzy` zmptwWk~@2*zThI7KDMXWH8)}r-^N5GvDSl-qZWVt#z||e2KC_2L>e#os8|21Am0rq zH{$)5+gm`0k|`e=!JRQXvE)J0{Aq%ryyvsPp91m~K4DVOO5&87-FJE0atZtoRd61V zTQ69oqJZHsXeSK}f})|68c;G`2la{CaM1P>sLmT_pgM5~D1q<-l**xV$P8l2 zV8a!^hI<_4K?DEn(yZzhaA(k|{Fe&if)rHe<)w&~8fB^W73!)WU!}R(Jz4OjHBah&{^w)m#-{<=O;CFGs zf$7&W_UA&^u+7$R70D7PFD`P~31$t*%`UuzV)@|n>uZl2Eg$3*qy-@qFM33`Qsd5p z8Kmj#$EYqhJnl2d>&FV)N=PWJlzif z6NQ}G@}K*d6(3o{F}i|n+Ps@HqFmur!tJ!uP=x>IBXNID`xJ_vwUbL2+nVd7UJ=xY zRzsw1b=Z^dfTi=}lwLIu#0t)El;#89w#BPHhx>V`GG_zl_gl(N`^;}y0zk_>Jh)xKefrvt7r97TJPnQ zJYE!3ptGFgTjdDh!&+mWP}ZSiH-J=2KU&0$OY{H82Tj%da|#VQsy+yj`~5-?UUWya zBNHPh5^0HFDrLIow;4@nw4TpHu^$oNXn**Jf0+2#$37;KOaOR20kYyeu)p@QKvs@= zIyqnY{gY=G9s=IlQkNU3+gGz3LSjl43x#+WvWGQYsB9S^B~wBnkpyN9F$@;<{F==g zH12Lh*?}DpeI6dkl%u%Puqh5Pl58os$wxbArY4JA9cz(z>~xazByc zyviQlf7rj;>rz`JZ56U`nh>>?GSyZ}q$cVeqCwJT&~0Z0;19ndQw=Cm!w~k+!dVMF50cwFYlB0Z zNW{TkX}(Z%HHDZ}1adMjggJz2lM7G6BBj(OQ1=gTFDCXvOTmo=zho*vr6Eii9wD;;s7#o!1R%%s6Z5+UwHcqo2I8C3Ah|-M7vl3;SEn5pA5a58XTD zTrzW}-9bWA##N0HLcY~2I;*dYgbG?zM{uy3UOKiMB1$RxZbN z1(A_=by(rVES6DRhAT0M3qid9tMv-F15@WYl)+O<&C^JRk%Gl`10bZVy7hO7GJ@hX z%97W$ww~qo1PLzp-6~Uto(0SrVy+%ug1n^l3!#9JJnHB{i|&5cPfox8t#|wdMbWtP zTBD}tE#CVG-v+W`lDqEc=j#goN`75MEM-<#Lw5A}$N1LY#o?U)s@Z5T4!&7KYeu>r ziyxL32cQ4HIQy%<5e^5yZsKw7ugT;oYtadugfhbINvRS~pR&20OKoURw8MSP~s=L9>R&3ojOCn^^**B@5$4;-Q0o&v~%Wu}N} zM{WA$k(u*>iwl#!Sp$pY>MbmuI5ul4m-<4mf-*6%GMXr?sN+)G11DWOpSMA@mIF`$ z_|fOG+VCWs%_4~YAe#@@siR-ua)&>P##S?APV-#wu@`+K_Z(5J7!Jo&=7huN@%oHtP<{tbsIWnc{d7&79_P z3cSc>@cb!X{NZCDmn{8(bc^Un4gz`)Fwi)x?u6=xw1X6N=G%c3OdBx(Q9g?>O))MnbopSbEGHd96EoKb@2^!51EJ0$lpDnS= zc`NA8fRvEil&UkfA$o_c`-PkEO*wF&Apf;y_S(vbF zs4=poUfX%c`^>IGevnIXXtQo}$Ie!qlbMmn|LjDA&{;#{~ zgBIP2>*PqvW*?iiml|K@dD!wS!Znfgwm6IXkMM+zZQgIU-uo82wtk)U_6*U6m_7LX zKHHq*0Bb<$2z%h!K@&D{dJT&q<;j1n48^dTt7jXpF$*KD}>*la? zlmoJoU-eH8f>=PP*yiLDEV6J4EohR%vUz;%zuzLeW!8YE3T^0dEM3A6A&Is=jm-H5k_C>~h*olH)~y>VE*wx8o- zhJTHrEZUs;<_k7^AKn^R#9GA;5 zm$nA7ZpNgF@#q=6a9nB78m^)xTv(JQ*ey-U0qd|>5+L=Y+!VPH!Z+T(`rTB=*4vkN z%vL2cqsKyM<{A$^_`nL%`AS=G`E+j>!U8Kp41}5s3*J>{rm7!6mg@90jzWQE=8RmX zJrUkJJec@GILtMq06c>#A%?_}CFn4g48#&G3d`*>Em&Q?`72_SR!k@?K_fqo77k-Z z*izn(D+= zX4l+n)twL9%=9KahgEPdbqcXX-2O-Y7i>rSM=ko-O!T3rZ0m-bxiKeqzAQ_PMaCqj zZ;hOsy6H#@eT>i1d?+ZWr-jaQHvO^V+zef`G2l5}KSTeyNhVsd%#@HE644A)BrI6j zIpd>8#7PdeKp!Ks+?s>1I)WtC9VKbOwTXZDYLCnz`mpX8=VA_Er#L7t4Lv9p^|$sgmTk1$RP*7^hdBOw0t?;f{>t-l1?{J%*D}e%PJtr}1cnqZ2 zW`_bc3;Y)@X05pehj>s_K@gKU#UIWnEIzZ|s=ddcnBsPv<*QA}4K&|{m$R1qHtv7A zN_Nw=Zw6`O>x?ng^mlqPx+lD7TZv9{0 ziJ1mBQ+4!N`6Prk3I^T$yu_~hSy9@Syedr2bMFE3+y@^lA@eB>!s^K}@g;?z z^AG;s&%ugZK7bcM3oP+BC^(O!T*DLk;0HhWx(eODU;D6CzxCwv9~s`h^JgcSJ&2-Q zkeoULWqn~eUMbqMU?oyoPzY+YBMo;#Jm?nwvL;TbNw5w>uc9q>6(Wz zKrC~qCu~_r2!)kLJx|^7mlpqN5ZX3;L#4ZH+nTlZwS9*$h+JX)t~yazH1a~k9La_K zJnn%Ig0^~xSVb%>{Z_Xq*B$M^Sax1mgm25h`thZ~Sjxv39o>VW<~LYjn?v@tJKpO& zj_O#C7Kk)%`Vta|;>kdU2I8Nb%mtgSMs=F9B&3yrA`{xNV93+z7(HTLFF+~36R<-) zHgUp|LSBn{=Tgl@hApHVt)9QL*vbHWvzz zGz$xCQ<$~kLRH(d1io@v5l~o8FY5RCf}*eRr*=Y3ItDkyMH7<$q?veHuv~Bjd()4+ z1*5~kG#0g3YBTVf2M*49ka+{Fk|85WV~eE);$iC)@%pW^jf()T!>pkbr*e{gC?9_= zxPn*|q>nler&=I78<)Y(C+p-TIdpOMTckavWTyQGk1 zWqupV>{r&>9(;Oz-;En=EmT$kgCln+T$Q=C2kcn47*|mTiyjZBep@+tV5pF-<%`fVCAXfbyXy1|&q8ttrMtP%NMRt}TUtpPqLsH!3AleE|k>bA)3^l&kZS zx(!Jd?@cIX3I6>JoUy_)gwBM8IL6}xNaN(sFTX4tL`tcH6UHPS3`FIh7lO{JKKdy3 zQ?nE^Ob{3pB1gTN$odfO`CZiH#uG(I!ZYTJ%tJID!8R-sL|tJDI#UqUrz%`Eig1zq z2HEMIhv9LYawK$Dq!4i!A8ie9c>{875?nykNiJ}$H$bF*Zujlxdi0ESV&0PB!q{23 z-xP8)9kBN+CfO^600t!2c%5g~Kt45DdEc6??bQQO34N+N@x4xxP`Z zAN*f_O&V&IPwkW~?Mgs7hLD$}zqZ{LKKvaPoF=sLu|Sx#4OlzxzN6Oh$GdHOu4H4Q zhmn7*vxoK_wqd9&F;$wYf~s(&7L!ONz0xl&W}wJ#lm{#_hM?6iKh7;)3J`4|gyXkW zZ4=p6K`VxkA2vi=cx$Qo`Jnb^cSEX zsWN2g%=ZE!0kJ5+R2RLr_8W(+6n@Ph?{e{65J|N)7Dgw3e##f}nXP*EGI=GeHO>z#l7xB3EXh;j9~HgzRKkigOU9 zJ*r)!OI>LB3cyw)e#Ie3N+H_t^`|+i{siByhPqBWW8F|#ta3;q!jUcL4Zo2TQJdvT zfMTfxpJldblXF9H#Z7pngwexevtPDc$L-BpV==_+p-xXnD=^9p9eQ%o9)Drc%Syw9 z`vw~CalfLi?P0&Ek9#M^5t_?=GN$u78c?a$ysc|{<6Mp_z(usE6apCV&b_JOoG00r zveh6&hR+8FF+yT*gjs{bflP8uF7f`_`8By^UZ5!;4doJ5r3MPlG*VQGCaWg5TNdOK za?+VR8GuHSHMm{5)i2i@mrpE+s2o|ji*;erN|o+@Gw8rZ^@*GD*5mf$OZY^;`5r5+ zej4_IEf5w;6Czw*W!^e20sZL>HuSH$?Wx@J_U@ZE*%mydPA)F8r%)~@@WULD(KYNC z8nLL+hHBn(d6sV;d}T1Jpy}7_yZCfF%-7npZ9=g>r+2rd+Awg){|kDt2%L zc`AAYeln2ewX{ewhyR6jzEm$*HD_AXo zU7A<6cj^<2={t4s;Qp&V`P$NWi#1z{(`LZYtl04zE&4>(#_%k0%Me$c;dgz!IKySA zAO)=LV^5;(eA`YgZ+#wF*f3!8``F+By~G##ZX;7XznvG91pOS{ zY{^IRz;)8D8(M>wHf?9-IitzCxd@Taejzy_V;)>TsO;6w6!zuD= zCE^r9DggEv^(!(b=GkxTt=ZkUSmn$jBom$ti=KTybEZ;WwBOo4YqwIkT+qZ>l#ee| z?KfeycJxt*Yubg)34XJagESHVR7fJ&J;YAD&xz9AO09LSI^E3tuZ(+uN|h*nWeJoGpPUNF;0VcIter%R2ji z!{*-mIo$4Iyi8k#=lf$@F`eM-BQiN5O&z9oi&cvq#1Z9`Y(1(8;Um)s@v-S%i+=7} zi@e-nSKpSm^&5BEk&yzY_wfvZ5L}dq*MqOH|FR2#{f}V;`kx(KD8x0JeZa>_%z1H7 z>WaXsZ$w?bh5d>+dSI#Xt1bPf-1dzT<}KH6wbkpdv%Lpr(JX>m1t)0@kKn8Rvmds( zANX&0ka$81&y@>4V?E$z+Cs#gzuPchm74ij)m&*i2U}WUWtk2}KQGSZBsxsmO@#YB{@*y8q$N zAHF}sSw?Xfn*T+#kK5LEqCM@i6X&=uL07ENU+^l9ESvW3+uLnRf7Ur1RQD91V>l#j zcKD^ybJ1pn;QO# zL5VS&2`ymcIkf?qklYocNx=q2GJ;03V%h4jwZCHn-UEu(+r7$W;IQ3drwtN*#wXUX zF{7?$k#30hFzFC>J8fUux`(dS#%{UJtfM~UA51b?*~R3ZyASy~%qDAebFvF60T&{$ zUb@(!vwywW-JDav%Y&R$H#qQV_zsUg!x_WJ-)6~~Q49(rwhEWOU0hrsWGGg#{atG; zaexltRIjZqhhtYE0QW!$zmNa`KmbWZK~(R2E_#q%7O?V$kac|$PpC5=vBLHHppZbc zK&u_?Q%`XX8cO6l5-5x6Vv>kru5tJ}T15a3SKe6kh1I;V6eb&~@2)oQN$iV$Hfmkn z!!|L4S<_kUq~ujqvkBOEy{P0LTw`6&{VW#y$Z<3}^k*#|0b-oJt8pbvLtMB>%;xQrJkagy>c(b6*C|4TTKFjTn;OY=7 zbw(xRhmEc!C7fr{gDlf{d}up}Pb#D=vAbvn2<_PeQ#OUEKsKn8Cd>FVJdb(F+% z=(J8)UC#akUR=&4zpoD1i8DSYs&Q$mNTZ!vF|v&XV2n-ldn1EFE{yJ2kt+gTz$pS0O9n zSsnL-2Rjq9@#Df;EPBLWAt4#G-8+9{vv7m%(vpm+4vVWac+J=Xj1 zkGZz%fM`btH@h(DG|W-455s6tD0d3#ag^(*a+p!zz}3R%@Y?-L_qjUN?~b-C>?xCl z86YGPfpy|o7l(mEHa<3uVIgJ0B8FHDxu3sYA|PrYaMxl@2Wo+fcK>rCE(ADC7#2&j z+@`|LtPO8S7k+M_AsZdF5aaMw4fiOH$|8Dt5U8*?#QmQfKLpazLZRWRgf(FZ@Kc}M z$hGNf9W|o<#qoEPPt#wb9aKs0_8+L4N*=`B0;u}uhK7&m9P$+?A&>UdS>XGR6l{ds zQhJ;_64xV2Bd{R70g&Jn#*YsT`tSHW&IAZCfn|@S;x&&{kF)kyD$G7`DCKHa91=aK zrdedvJe>wCuo!JnIYL%Ccc`|zAXOM1A4HAD|6z!PtNiyAcb0eZRNxLNE@dEE%?0MJ zPKj6qC%pqHG`kUqQ)hDwQ8DAt#)i9utVrT27Gefzh2}9#`Psz3Yqte@viF)0LGX8J&-UQCB z$B$}w{#6QPlS;~l! z5;2ZtkK>KBo!FAYSn`OVVu}=L%A~}d00@BCfd+c-x4-%Rr*6Hz{kj|74S>Y<3V40* zt>x6IbF0ohb?Ve9`c^{jWoQlVV^B{Z6l{Q5_2d3SIHmE2bwd#*%wW^dQhEIZ47%{T zX)T+orF8eqK~+YC6w+QYLX4+?A)qf6H?TB;QN@;~(_kf_C{4(#wgkJ{;Q?08G7B!t zu~=f-qjf5)SLF8a^jC=6*Usl!`(GhSCrDJOpL-2}*Us=|CR$0d_^J!BD98n{F#$c3 zDZv-ru4F!mFQ5kc86z>}YEe1SMw?Hi8_>M~i&2KgPSHCTv>{sHQ59I9ftN5L@ZwwR z`LoeKmQj4qn3H(_tbM`(Z{esd8toA3y_u1~z!u(uTAOaUgAGYPLPu|ipdO6Aq1n^t zpZVKPBiW|Gw2#>`+9xCfI`s%27cc1uKrBF8F_G4Wc^Yb^QGl&3Zq;9Wy|Hr9t@?->z{CAM>t+` zUCV9W1>Tr^1o&zpJjp%%EX`_Y8OtVcMhhfw9aCz&MpIvnd0mslODb23L?XKiMctT8 z#VwKtTjNkDL(n8&!S)K&Zf8na?dXL(aYeA@N+-b5Q{r zY4O1*E=^7xn{&Vn*|_?CzOW9T%wNq3G1YA#sWMwe)k)`z#;)>+m*|>My;irbdUuM| z`0-aBVnKQQ*s?{A=q>vN56xO&m~>^$Mb2mE*T1#w)0CT4BhsE#-aNDz4vF3B^pD z+lhmuVJuy1?5TM_)2pHsY5h2zdq3 zA__*Aa34-qw_F=F04#!`IEWjjnhR_6PxEbCRC=lG-MEWQ zfey0A`|O2ObwTfyJ|p453lKt3V>FCZ649qBs9)KSENw~CQuHU;sw6VVDrwHlRR(lp zToW)it%gdK)F4jZKYa(;lYpSa_9&*`Xah|VFDST2=JW~dYO}dU`k#N|6X{+lS7ADV z!3egLn|a)X*xXK~q9D$=&$y|XIvAr-k~VejJGkH7vFYzp_PcOtE;B!{h{F6GjEPkX zJA`6ThL4C|Q9kL5%;yiHsF>!Oy)}f&uGQ;OTuB{dsrYM^M%{L=iUQ&X;Sh>YpuzNs zBfGqGgz?y4?65|JT2XSbG;0Q;(LZNMq2zzalY^XUb})yll8sJ}@>hDIG## zlAc=84XdO8#L@~9tCI_Zh)+-9u#9f#QeZU{kkBHbq_@A|c4aslAy!CCEW-`~XNOQM zW7a@f25?KJjwcR2as0FY;-~k$_kADE%vE&InCCKoriB^|fOH~{?tGArJmJ3j06M(G zq=-G6Q5}@1ci%9{Kgs>2w zI4he(F9F%7kMWtsET_ooHRg2+#!b9zVcIG&D{-ZI7XJY2H*WcD3>yEz*S_@Si-um> z?cTjWM|{4JfBKF)Qup0=U*-S%%AX8Oj!$A|HG|u8FSQ|RvH8E?4j)Lv|T2DzTO(n7x8YnE(N+2Xj4D}l0pr$hO_e4V5{L$7oogiN?2e4#LOCS zFNA?eg3Ztg7F{u>wJcs-V#*}bA#5}KaspcpD9^eBa3~d3vEW{H#?$oN8H7XH;nuh_ z;e4L&+p|ZyGxxb){CV63xPAOIpEQKIDvd>KH*?oVK2n*PsU(a2T5Z&Vrx*E1<5?L) za}?7JT?%w5a49Gt^CTJinpuNZZR|gGQrN9Xh&r*~PYECR_5YX{S-;-ZYjW1$1r)L; z@*E8O?eE~GgmCY@_v)hUHM$yOvYU?k&a{vxe!VEtRRqu&7Wbcse(`$j9u5o6iPoFn z_k&V?(Qw%%shHPd*&!4Qnq$Sff4UUtQs9lI0CIx~6Jk~hdXD|ZCQN~S^xyu{FMJQF zrs}bL{*R;%2`E@+?yUio!zy9AYu2#L3xuR*qToAC=tym2 zm}N?Jqq`JXH3gzkt9^szrMxw;A5Xd{cvTqRD>0;yZ`CNAE8|6DSYlLg-^SC=ANk2y zxAoK2W^$5s1DR1PurVUp-C9AU5a&3rTcgu6wtJllFdAAIgl{sqPU zWe{f01C>QZf{SEnzLyPRQt=Rf4oUay8MQ2}W_7ON_o`NLFyOt3F=uI16YR$}KLuE6 z$RCz=t}i@01f@k` z^pnoL8|i7uK!NnwO!e=~)SEK&oI%BAb1}J$!L?HI=)uX1 z#6o`(gytGqHu;F#ArB?1)oLZ1O}|6_!WdRCC~rQmAtuN)Q%LL4F51)>%or4DW(|R2 zmZ$q?X$rL8{nE+Z*e(UmmjY4OGH(rdc+ehUAMhB$ioW;O07r^kB(v#8j!f|N*lY$> z4*mvozGlkQrVPms3_mMZR$K@?dNtX5k z7_lccJ0@M~iGSL;6V*nd77^njsK=)Jw@ZO81>SH9Fm9#G@@}S7yM}aoIKw$i-y^+^ z-+rgj>+M1{qSKPPHf+A(qrdZ|r#7cE8FX9{N8oc9pAPqz$kXR0rg4}EQxOLcU#u|Q zym4pAZP<DnEAY$SdjDnc3nvCxKTg_`{4iit=*kjGLOAbf@^?kQtAG zelzkJwggS#;H*+1ZEDJ8@GUNhOy`%Kn647vpF$+miPj!PHC)VzXFHl*SS!%F8V5V5 zO0`4`O*^`qao42E*9}lW)aiyU1>Qgk(B-__mHECWJIQ&0V&2|dIZP0XvLbQG0;2W& z(fUn)@a))4TR0tC3`jiUBtp%H??~aUYS?}AVABn<<3;>N2UG7ym;VV)@!pIhOKIwZ z00rwXqpy<_GY#pm#4m7l$t7Y~syCpCyBkABo)hGpjm_1TO^s2Sc~k&|()5!kIaJgZ zo2U`M5^vT43e$65no6@XIsp^xFZN&xFaOMvw$Ru!MGPKCBQ|HujHGKQ@E-IoI)ls^ z%%q_+q5IsWz(u4$#0I2bpGJ?B#RX_!4dxb{eT1A|$DGd&p)kL9SU;G1@Q8g_oP?Ho zz1rmLhh~L+u9}54;0-mD`ExUznBAOYmc~gbCm2DPB+qzDPmPQ+b9s=v|S@DqoX_$Wzn!*+CCj4#WjKM*00ldq$Qi3)Rb z`#9?S9q#)6VGaP0hTd{oKAAgAAi2rJ!2m2Vc0H7<@h^?Vbx+G&cXTuydbI4Ml8*SR zkqq57T`~%2C~DeP!nM>bJJ&71LqjsnmYe$A8J8bFVSDfTb>_n$;~lQ$r7vx{lZR6i zV8j-ZQYC3Zs0Z!UGoGS*ZcJ;WtNoPdhdme1KRgDl$l@TKmc=>vA>A+;mHh!hBAPsk zYO~`serc-sz>&vM9FLmccAd?q-!$=MHbWZs#FNZqO@3~g#x*Oz5Mm}Gajz?0$pzjG zt-l3MTXu&|9sp6*sf=nA5*90mJ*9qYz%?gc(S9+jMv0B3>KzK6H2z+HL;QS9_pVEU zi$(!49TViart=(}LVvSyOh=T*&lGM~s+^pqF6#E<`Wa@meuq{SCDwR0qXlfuzT`bs z-POlNU0DyYAlRDYNg1{>%-^Sr#X_?O8>uRijX0o!7*+`#R&P(y?Hc4{i+=P} zFf2J=$mLqDpMw^voM^mju;GR|_&_3^laedDsqfv@bw3?4L4Sv z(-{M8z9cv$7+ygKDTr|G+}FxYL@{GZ5hK6$h;XgE?8~y9nhHvNGdEjthmM>vrMOf& zVn&j@ilu_XF(2+_hQWV~nG~L_=&BV?_%R8K{;Do3;ids2nZ$p38OwqyWzj#e9glMr zs6%>h9a>YUa~v%9sJ&X~;B^v__!oSV@!uW}tU-c;xk!F&&)1Uc_indy`n*V|+){!i z-072LcMGSYX7TH;DVl^KZLley#v^ER#@&*y8WNK8WLMS4vE4l!3AHAt!n z8OWnY2`6m<1$HC$CKk|B%(w0sDYb@CqMIcFW4xCGP6vC7EV$*}jv?7%8XwZ$L|6dF z*Yy-y1=bYBFM$x`cb}baUfpKR0q$Vs{C&J?4U} z25JDtxt1lv0f|%tExWQ^I3`^PT?%w5aK02^C|a=Ls!<$8L<7?e)mo(PQc*_?62dPK_Pp+)hrz?d1e`tsJVnO07pCd*<Cq6?g$J1!P^aTYCkV^Xh{wM!L4%#m` z{L!y^5K|MAp=crb)DeE@zgStCw1F6s{70MOd&h#hG zo;uVTx#=!ykQacdnykwmv8<)7xy9W=XOJTD6XQu4{CYz!IkcFo~ADsoXr ziDm2Fb}8`sQXmXCI;S0q9Oh-zs_|@NnrmbBgt4tN$B*Pny?w4yF5}A1vl31zPdEDd z+`$8{jtJS&FMcsU`cpqOZ)th~EfB-fXZ?Z$%*vX2bawXBht5p>G15YX(dS#Bb;A~R z4>?(#O`L5dv)LN9`;)9hU^YhGe&~u}$#Aq-=z%a7jMC)^paoOP_Pa)bd9_JYooK4z zq1SY#b)UNwczr3LaVJTt)RT8xw_vjn3tFYqsW~m&%j|o}rEdPAzccYye*7o@OV9WT zwDY)dU?A&Hve{_VGKGQ4$i~N19A+g8%!|TjVoD`}zM!B{mZ-n@^f$Qbw`@q_x}JT~ z90$(|WS9~{DcNwzgF`k~Fiw8;4cowlxkkKvSt4~4x)its6ws_sQq;_J*_}8(j=7R# z4jbB-FJUK%f_kuM!cy*~Kl)XvlRi4VB%UNqoK%WB{Y7C`0(LPOmT~4V4HWR@0#hIK zatK^H&eeD_IU$m)UDHe*H5C@&BEf4pFjsiI8hF(=@(O=P*Dm@Rf3l18x>XHN6wy5E zr+=6-ncLCP(JvHlz4d#SneIK;xaXdGyw2w$k@BK3EW}9oO`uFD0+GsU!`ba8@t9DVS?2NQSSefI?ceg1iq)~a87 z>|6iK*4y9m_fAhDzD4X%4j3?AcPCyuDjD&2E3MvY#u}f5C}LWYso+@_XLPtYt-I`? zsBKqs`z||zcM)1E1y<2Lm1Sh3Yn(dH2^4>E_~(oLo7Oit=Zn=cD~;^ZOtvbe6NkR@ z>oBW_VOl24tQD1|@&}KjOQo$f&bBtcdidyIZsS|rx!K`6rNQRWa*&i zw!1|iSan+(6Kt;3u4ZZjGpG|=PEWgk*3*;3msQSNrjs3q!gw4lx`W5qDuY?WLl5oE zJn+B+v1wz>=emH!14&AiC*zPScHfeOH8MFlIgo3ye8mcYg>u{LXoq?oBHS~V+` zB$!DhrA>vcGIKg2S<-}&=99(LP%vN zsn^}YDnz11J0k8Rg5+2wOt7?X`xCEZvj!a)VF%wvJwSwzBq`H{Me&x{{nMqurKf<& zNPb9af3SYWt+*s)0Ip}oYu$SsZO+clR_o&~U9Y$B9^(fxYOea(q|49E&N9z(?n50O zM=zw2>6ee6{ryDm;JX@)#&n|Qm#&()o+)M%iRYg_mH5-ArkVpKyjH<#gw9;G89T{nY${#~_Y58! zn$-z6al*VGYusxgQEQ3HqMu1xy4k*M%eGgKp1!3#(-_R=QuTVh#`G!E%H|TOG_n$` zq-k*~q>#?Ea7LOc6f)?ETCG|gS1>+7im>!tWo9gW;>_81jjS8wl#Eo5a8nM>k*pacNMmbYQm#*Po>eO7%kA3=gp82s7 zX74t=1nwM80dL(ogbCAnci?0NyG!pNP_we}nTGrD_YAn3?&w1>g7Mk>OskccJM|{r zfmdHUnr=1NWvGQmkU`?poJEUwus#{OHrmrcO{VQ4!!wt0|~ zedO90aVl|L9v3q(GvFt55M$uZYOZ-cmaw6~EKp_*1y0DxFiQ*!D@DN!ntBn;1dt`k z;Xzy(f5me3yW$&iWstjz<%Lrqbk%T;L6kt#*_A^N*HuAT{Mb#n<7emGe|qVRyR$cE zTk*8f`5-QmONA`{_4?c+2XS2m&k(D~!Z)5dQ+MyYv+Bz53AwD%>{CJB8WQlMxq7{b z4hu&#M2Z9I)Cex^HAq#A)PvO&imZs)2)N>X8m`*8x9K8K*gXg%g2`>hra?@pT_G0i zNL8m$lPUD#e+vlwGs(|}G+;bjds6z<5F!Fx4Nd8`;o6{p2BJc^`X~Q49w*H@LCiv$ z=>%exF*J)q?twvM@uz+hEM^e@S~dJ-nP&#_<+IpY;xJ3hsz2t_eFxS~=*YAq2ump7 zssaL4v|ulAr$j?acViSj#be|dk{7nJl!EzDnO91gCqbUYj6#~&s-}xNMo}x9>3)fT zF=hmd!Xd8Hc|BM?u z!D`^TmfN%oX9g8_j|Mw09=N@^o*z6u8_epl%#>VY`Z9=tgn;(zr2~$I&KIN|Qq=Fat1Ej+$jz zG@uC#Z{uTf=%+F5XI+9L;Y2@UJN8#c?I_-=fj?lqMZBUk6JymK{*Z2zXY0v1PGZK# zx>+)iL36|=Oli1KbN}Raj6QKRuN4C6mZ~tSt?O9Ls}b3FzRa;2O`Nj{0F z)xrv)mB_g{IFpvwBx#9HO(4A_Q`tn8{nd3QfLeM~XmXUGcJXDi*+%*oe(v7%Kq+q$ zPhc?`dS(+<%s8F;EY23VkJ4T+QfO2ciFX} zi%^#Wiz%QXt1(^d&AUyhd2bEs&fcs6Zc;t#%HzSTA%dX{`h|e_v}G`n{E=PhWQoI; zgTPb4vSCX;&mlYjY{XsS_<=t^_J#XC@yhq!wL61xxeaG&)Sr+@<>sg-5>|wk z>O}y0^+7k@qW{ttOF9vl1Su7I+6x|xR=F1 zHm!5wt-Rzk6IS$mAtCK>YrGX0`|jIg^4EXpsCa73B*nQH>fGp*o9CbVjw0O0KK3zPWAi`7o?ke(bhS2^h7fwIQHuQ> zzeQrwQ%^mWdh1)?T6^pJKb(~!Z5odhd6p!7M(=S{Qkj%~#?9AO|Lc?+>^fUw+*OgC z39l0hU9epWyulREOh*fEVIrrKucSCvynbU7PDf81{db@G*Wb%kV?RC^bT0wrFh6(y z{r5{Kil;03yu!s~ScU*>R){Sp{uxu7E>F9yr5<;CRx503i)Y_rR5EL5aR|j_@Fc(d|PhXjU+5C|)jR4H%d-^TXzO z!5f1{Z_}Ek=JC#CgwmbD#$R;z{tBjmaTf%KBHkJ{rgW;6@ALZW%^DD_%4gNWj`6Li zCBtg-Em^oT@}e;;7?t)vH=cdzLdTaRTKA`?E8D0s=pph6&WL zaz7C>YY;)~5DLs1a+i%+1GF{?OR6|ws9&Ie{GRA;=u+S+pn#@(X4Wt{x2(5@LChM8 zMVFYxIr7n?M|bq}^xz`FzT9mJNGRcV7+ABUaS4;lc9!$H(OyCuje97*I|32z_i*#b zBSgNNpROV3S=#nk99{(lr}6kR-}>Z+YL~*cmq{7s(plYD%6B&*Mi<{Vu4OdvEl;G-yBrxo^d34}+sk%3&Ks7+AZmktEus1)<9mrfGV?wHj|!jLiesqfi49u z0R=QFHDWbQluQv1>9lzR#U(HgGA!9DO)*=-$2xElNvBXoC`qb1&T9Uem~ zv+m)&<8GvYyJL`4+L_t9`_LUDuCxxcBa^ClTa8@-SE~LNW({-ZtwC8m!;%h*mRsR= zUgf_eL2szzEnN~kx|Mb*@WxX>Ov=m}#0&^#)5lVgK(2( ztFY@C+VRnU|9ekv&Sla$mamXolIX6Rt1M0Hb$X)KOv^77L4W5e4R_~FJ?ZVc z`Wxk%^827#GwDq3^pQh*)6+9EePb0@t5!@gW;rBHq5F1|6BF+6*$K{>NHM>YoCgzQ zOVIdW7XLTnmgXvQk&IgyEdowg-CVgZNmh=CN`3hhrlo$GX#VSH*yvuW+cukT;;S6 zA;{cL6o^-v@|%f1l`2mk{1j^{%j3s8UKsHDFgxwpbuUtxyfvJGaiw&M3=jmvj>6`s z`W{ZydE+E3FqC?Y9owEe2@@X@a^KuN;U z)>)kQ%@{RIxMB8HmnJ+KI`W0bbD~kJHXG&XW<8xXhNW{yo0XcY%ueG&ZxWt|qy$@% zb3Yk*HOmb*HBXMbd+dVEc54FS;S!2e}``FA9-WqVdS}44M-WniQv(S&N6?=u4k_MvBlYj<@ zpo2_Sex*P~X~B;VNRQL@;9e^9y?RP@*GRXnOIeo=IED04ckEi&SWvMiSZTtVI+ z#C*h2XPMhoaTh9lG@j+RU6N3pv>Dharl4?7(vZ`pc@~^1C^Cg@i*z;|N(0PD4xy(SOt=j@5xUBr z>1Z5@>ByCOn4APgrJ|CWA`r$OER6yxHP0GVy`Y4g^Y`guvC!# zG-d(+G)^oA={d4-X3FU4(uH}#Tsj=OHFqg+B~U=eD$m#+JWlj-RU6@qagE`K&q$~_ zjv{@S6+6>`!z0ch`BWtY!DJ?xhDmoNfhmK4G*(4;;z83)>9CP+G&_ZTFbg723m%ky z_Q`K1PoFr!6s>AvnMg%Ck`8B+ot$y+-&kc&(Y(ad?17QhL& zm z#eWX8M2n8$WvLoD@L!vL7D-O!~#mjX*t zV1Bq-vLqcFtR0yn=)>f1Qh9q6i_*h zFss}9t+YGxa>k^rFgk-jy?y)kmeN1@cYpTw(ngpK5>q^g7oEVj^1c((#+PIPrZ`p95$CtPLGd?JDE*|%o@&4atMX(4Du5}EpO&wSQp={ z0iudy+DSGY4S~?D?g=*?MCm?Ws}#`8Mv_!7-Wsx-v6D2_ji*NN*3hhuyED^v2t_NO z&)0D#%x=3>BV=VZSHm2taQ&_utej}YbhR!Q$;k!|>08CBJA+w6R@(VU!qPmqj`R16Zq^{Cq!Vs(=+{i5 zTB4EqOhkArk=l*wQsA1PfQErq1E+Kd#c|9U7`GvaVdaZ?w&vBD3E8wHb@H`?xokeq zDq$J5lh2D4L3*IC&mGvmcUWaV``Q1R`}oH{G;bj;mf#0lxGJ0O6(r;=doPBiInf0N zn3YxX%JDOwe`WgU*Qpj$*w9*9+Q2JBDJ!JUVo#RILzGP9ts!77rMn0udF%D#AECAj zZgLkumjYKe1p?cMS{Za$8k_rGa_QUd{@=#`>iggIAjxqiquiQF^}FGnkLm$tB~8^u z;WJ`b7b|#9aPYaW0Z_li;jE9MHF9gigEl_@SD*?Nh49ggf8J7w2IRYMSv~iJ^;1WViOHJa* z;zX2g=u)6dflE#S(=2N^dYK}IkBw2qy3sFusrdG7x0r4^WZ28i4g8*X;x`+kqoZCs z9}+L{deImbh{?Nzw*BmU^=Rb>neVm@yNE;ko+$JdQ!j29&U`hSDI{gqu#~rk3{yf$ zS{ILRiMU-Rc!B+-Tl^ZMfMz;c1mjRETCSN7J73W3_!*G#DjQUzANxq$=xGs^7|XB) zbUha-Xli!O5q*~CNki5Sp^!dVvmfFxJ(oebW4sA2pROF6e`qs$uS+s;6C8CC>a)w6Eyl$nfg9-B%1LZuJyE46fciLJ&Y zaQ(Wp+kg15_U}A~`}7PfpeB!@^U&_*R{HwRKzvBjRe@}G`+IfAYAyiCSJCQZb@u}Z?==q$??AheaUw@D)%%_=Q^4Y?m zjJO6HoD+5YD4$+r*3fh%yfx&{$6EtTsaY6utxU-n)I5qpR{`ebVZhCsUS{-w#S}pQ83PeEEuvRL}%+5`z zf)9P@M)^XxC?UP~rYQLdyDvSfV)aq zDc4+U*ZNGdUOSEz{7kcuO(ozJ+Ng-fz*_6pog>8(=pii0dFX-wF+8J#1ItG1qI+vF zh6K6RCNM6*vHQ@az%@;QAc{Z)jrZ82e-07e--{Gf@<5YnYW>U?vC*foziH!!%`YA} zeqE)O8ce067{XK?{=$Jeq+-a0jG?%A=*cHi{e5Z0YashTnR=q0PUUi^(#KAn{=mp^ zzpIv0gMq0UZ}gfhElkZ@g7Hy%(dJ{Tey#@OuH|>FyU@tV`K0*`odJz z{cL}qTfdpgyjsdC3~gM{da`JTwEh}dU^e16NPH2N-Ak7OT?)M66bQqwJ!qH1z!v{3 zmle?;o^+A0Qk7@oTK|vT_Xpqk(Gtes+QK9EQFGP0d(*a|M7_Dr9XwrYTU>&3E**=J4F0{0c4n#pm*=%+Z%$*_TFHhIu(<>TFLmjzwusgJ{+zG&S6H z*@o){sE(AEFE6>7$)p;t$%OAZ&Qu&=#|uUZq$S)g#4@>yU{;sVK@{S-YJdVRwIVH- zJ@N;m25RU$gd!jra&@m=3Un!OX(%9ep`XTZ#5-b$AkoBv<%yoPjHhYk$+L6G-+y_+ zy{Q+M>e7F~BVL=Sxn8`MR$vhS{b1FtGZ)EtGKQZ#d#2&O_fCB5QMQaWDd@48-K8Xx z%~Wb=#(+&_rg``L435XM>K5m#@e_B1PqD-)=H%m*ro$&ByV>$5_maZIfOc$>&l^95 z6dZ+oKJ89{v_daxFDclk@j9BPW}7KeqYk04qp<xsZ#W11QsF5}}l)ifPemWCZ< z-l}*_|CnJ0a8{?B)C)0%H2>@n3Xa1L-Wn8desAzgQ9OrPgZH`sBAp><{w+RgNN1kz zbC&{L3Y?Py8adJ<7xC7xAtm=u^Fho-W(_)o!i*Y}SF3Y4k4fN+Pt!mRaVx_g@s%uB z<9Q_LigwA;B><&iG@Pm|D0byMLsjmXmaaT1m|k1+O!y(9Os$p{Gik^$T^o-*V@bi3 z21*I{!#AW{zqvuybdQxl=7^hz@gGqux{osLD>C^M$CM81*14J+>DM_smQ{?{2WoEL zD+a$S-2+@ko#Dg)yiIe)~d&zRnM`4>$ss!~1%nd5QDC+Q^I;t@lWC+s`{`fK3=a-~ zj6^!0Lo7tf?*^-ZHR8uT&-A598++D{9W!xmf+B_>%ZjH?o^*F?{2}I{w>Mci1ih%n zsAT*i$u1vxYp{6L$1ejLmTYExXUXD38lFog#;fSwcPY@Nz=cpC8e(Cj8Gi^50d9Ea zYQr+b!xnDbSV;W*+XhpG9OsNOpjEz#fA_5$@^+BTCgcvY&!mi;u7BUQgc~YiH#Jki zu*(Zb;vr4AVJ1;@Gvi~u>EHj2U;VlHIxgU@Z%e{W9;v+d1Mk0w>ExZwD$EmGGS9S_ zCc;%0m@Lo$xTF*9^nzd?)loaH#0WXQ_TVH8#ereeRrhu(j1IAwIGqZDN zv`YBNkZ?iQ$s<<$x?Q`rvx<3VB8j)=W+DSq#iBpa%4J;=?}Vn6!x92RFv(HyGnkg= z%sj(o726G63Un!Oi6{^oej1A!n-V1a7>$bvewk2`lhRFsicm(gRL|sGCR^T%OH#0Mn3mypNt397D zxI?e*)5f0!C%(3tS?{fZLo?d4l9oO*=Q@NUM$8LCxLdkQfi4A>q(CI`Xf`A>BPMI@ z@hOkM`iViMQRfg^xlFpnjL#*TNp{|mB$!JBqZJtc2|7aZ>U z_)`*>+^7EKzc|F@BtN<9lLB5rh>I~!-}(JNSsOcjFu7&pJKfFrj5f0dNmcxpZI;X$ zv_z?SYYL-`1=Gm3{CII1FT}c`OMxy0<|z>PipE|~p3~tY&9|g2EsEvfPTCTD;)OAd z>ExFl`0ez%4I6cmJ8Ew~E?doJwpc2;r=NNX({krN_ObiL2aI{?`9i`)ANjbL$=-VF z_ooiK!wc}*>bAOr2lpob(Lef|GvBrQgS&foyyxNLQ_{=_`v)o4!=7^vLZJ!^e#AHNs*X`sf_c|5;ha^3kAUAhzu3rnrhr4u6# zg9Z(64RwbS&FnNnW{I0M-0EaTcDhDykLk1)w^EEtv0Y{r=+?bxm3gRv;Ve2-4~5nsUDlgtx(}wlZD`tzumic+Y}Lk zn2Y2L7VUEBU_Z~i$T?%w5uxbiKa)6kY#-+{%m5rsYa^7d> z5|Re9^0LGYH)z_YaozcoFeLu6Smq$|e8cO(u!P`}zmbuVHuC<)duy=F{4v3)-9E9a zcJ}jaU$mdIKCcEBl~GvgJt+QsV6Ie7>C1&qeB$+Yro~~z)8b{6C!Q8Bvs7BV>{`mj z;VxA!z+EaWlm}{+$FqTGKM9)4Y`<*J-qs^Xe6_SG>UgA{_5E4 z%Obxsgc#-T3|D(vXL~!J^%bZBw9hA(3wOk`vPzk-9o%*_Ijon{AKDpSDNrUA3T?WX>qhti=(DTP(()% zZA7o7!a37K=VgR<>#uKQ+C^+b=o1Y-ev;VbNhAzD(I}*!?|V9*a$$emQvKpwY3JMT z&joX-Gzk(~B`jh6-8VVw`xBL`tukxKGd2?mW_y=_S?Qh8Ro}P}T+ZX_G%?FT(8p$4 z?!;`%9p|8+<6KX$`SI8^dx$WL@9V*nX{`#u>I{1SlOOmzd#g)*StPLeH09BA5$~r( z2(9~2Mpr!-ilp^qmQKUUtt8K>f{8(HD64Y$LJGLcloQP5(u7M?UuZ+XvtTfNeW9*- zJXK$MF`jHr$LpdS+1y9RVG}`&F5$j!umJvzj&z^5Z;-g$bR8JT8@xDN-79}T zFync3uQXgVx`CnMl4-(WzT7;n0cNN#4mYF;&v<;iKDBSTG^?+JE=G6a)^F%-zl(VJ z2JbhE<#JD7&fa}IP4Av=>U!u;@qW>Mhw(lWdlxEK#96?icMmPCAIADcXMK64^=7Q! zccv9%eI~p|U44CNS6HeaGJb?dLH&^XPM9iFi27RSqvAxBYQ^PnYOrJHh#MN|btCKh z+&X^i`RO^6y(DsuF{(>ab0X7dm9r%`#wj;Bh;FNauy7Mc5vxY9qAib4y2B@@T#tE! z@RC)Tjgw;eIk@~$(!8e#xGrU9s|_SFzM_meU$jl_Z!mG&#XW3e&glTXVTC|zNzB%87@)3*E?3)f^wv3#-cZOBnDG>YD}mP zDs=X>D&`A08qt6TubrB91!$&FhgQX6fkPv!XlMqh6c{ zCGk`PBn&cp0iG;j)2R?TA5Y^A1>YF;4XX_Tb6rHP4j!c^_PTt}KL?3vQCbY0wk6e2A-U~`T7kNPcwrF z^P#>_Mybjdmd&&BLlT3TwlrU#U|LnN`otjhj(ppFUE!}jVu3dHE~q!WL*=n|NS+1n zDMHMrB^UM?2&8^teTH|i=pBNqPg}Z=+Itg>cpud><*ctQflH`QCO-@MZ@ZlDGYfEu z(u9j>*;!8XwlossxQHfF1EfSJ7?(aGde@>EC49dF_NAHlvA1cd&_FDJZ=G-HoRDx;h+MAxv5e1ZFl7eN@(3zJSES>_oik#s&5Kq&;cA76KUf;2r_g!dn{AT25{yoGaTn!$|9d+PeOdYUOlebxuN>Q9J>;@{}Y zC1F@;oc*bz;T%wV3EO1RS0Ho;=^mtC67h86tOlKQSX%LEaBP|G^`i7OXs1aZ0R-uS z7Ny~%nF-FqG-6aolXC26w0q68gvUadzHX8h)+d;V=?icHX24U($I~>R+G!wa?dyaI zE<#>uz74*QRH#b_9I3t1XQkz8EDC`JYDX2lgO;Pi;BBWt!y4sGhb6$VVC8@>9irN; zAR1^opg8Gz6hbNgwI+|!cppaeD3M|=&0EzHc$G9R)((}|y{2eND}!Tzr!Q}t}&?6S7r#b7pBK`Pi>e=`iiC1aO89fMt0~Eu?Wmb~{Z>rPP~unOx3oT#uIv_0|xKaOf#| zOKx}_or>k>z<2^qg&xvIVCZz>1pzR5%8jTTNLo3;Bcldqp;UD1hxLxaTT(OS5c$0<`^L})2`j9Ctjq=)mG)UuqfUajK7U_r4 znS6S-v?T8h8tf{lZvzoTtv*KS0E)aM>DS}2CCxE=p%HcN2a~)XfFufh$TXs(2G)rm za$5N&InN$lUVDDJaAJmg34*ZlA?7fm_BPpOviecvbOMqh`VYI zu|0j90mwk18WMc4qqn)8nPm{@6Bq^Ec@T-#9{{^3++7^0qw3upR_(elm+WEydyO@FPMaYsin>N zC-sY#F$2Seb42N*Gg{z*?^vn%KPp!V<)|;fQx2^xmXE17LQ~->f<+lLmgb^z8ASu! z0{5yYm0RbEl{vRP$5{{7DsTcuDwShN5)6U_~wy1W$G8bFX)=(7>E^ig6Rt zd@O%gfTgx*BWPPZ;DN<-?;h;BP_8q{`Kw1+{+u#2ahYtPeZ|$dC2%EXNiQn3A>b)&-_dopP6AzBwqjDh# zCo$Ev;FQ_A`|cx$_dFP<(^g>U-K`BLw=Fq3`u{#QI{F^T%ptsu8x79*zzk_iU4?sQ z=R1Dn#DT|t|HW8_P2AtiZ*4f`uS@>G|NJvgJapg3)ejZ){?!lG0teCUmWG>q@i762 z1u@mX%x|^yR#D~6-uVN6eebs)`pv_NxQOBP8q3R~s6qKg2N95l-mSrRLE-MZojY~g zZHYajqxHw0dHxH<;q^Z-F+Psi>ID;3Bs;IUiI$t1l^sv0PV*Y35X>8Y^^2c!Fsrwd z@}yGtJo?RdX0? z=oj;&qd!&N^W-!CeqiI4ADEn2j8l2Cg^r*TC#F&87JluEzvN(6cLD2L!s+R!pMG1d zH1wzx?>1!#;yXyBP>7#FLSKjz_HCT+IC*-%`_aGh?gs&J&(D1N(}mCcgMTphjlX#A z-;_o++`T$F(c2+nhP`FK`#Ya=UmU&XJ;2(-&wcyb-@aoyS@=eawxitOjYbsVk^rZ1 zrf&@?b(NVA=ydk<0rwL>{H{MH>cgX7gib&8Q{_K>{HcGle%p0FGJa+Z-J|c4dUq+_ zU1brSJWevU{=MJ3-@&ZjNmd)!-+K1hH*;{@6EuDiPAT4<(J6+LbawJ^d-B-J?r;9J z_x%}g-ZeTp%DWpaKl0~K|I*Or?SK93*g~AtpQ`M}7@vy2y9g(A82{xD?sxz0mp}di zVErjS_sEk^|GWN;Tfcv)J}^7pa?^8hIGT3R2cY+_(+7V0S6{ph5YK-RI{g%Mdg_^H z-k0nfd1x9xa>4z$>JX6=75e75may#6qNU5sq4PX;@EP~FfBXkON96y(FX!AS%9Q8A ztFgRG{$YGA3b)2c=m{Mr_pnvD_(#TdfsGjPp>3~Hcq$RdTZ+#gK&l^wDqzWPFbcrXUNS$Qj9sk zsKKeX*=XZ*%lCbsNg-QqdQ&2S!WW!49@kIrgp+*hx9Jqa30*(d-p&p752(qK5-i+& z^UaAPF594bng%IiIHif4pf;(Zl&By&rPQY2c(YMoXps1$5f+raP+%&iBmG z2h=YuFJ(mUu|v@O%))*NoK_L%c6KaXq=$HsKHb5a|VsC&R~PCGxCjlwiofnRrmVURIXGx z*dpsHW%{bbCkwC~UMq2~UP(EG7ha1wxB+BM)tbZVnjDI*Pu-ci4_wkeecRVtVdl<5N*-(E%2wgf#utKPy=Kf-hN zccwDqD#;S>PUk)NG}X<@XDdlR(7Tg$hFY)GM!k`!rqWBc&j4P5f36ya#cQ_}C|$#A z=8I@^5?cWgT3`#R6R4)&-yph)58}NigW+ z`Xhh_lZNwPe~eC6&tgzSv-;?Hx7|}p5UgZW#vVlo(bei*1jolJk5wA#7CI5Ekf5SO z`}Y!M4&YdDP9zas7_df<&;}K-`b9*0^&W^(lPIHC01{nk6;PCq)aXu+HWdrBvUJO$ z{$dUH_|9Xicc5ymoUkTabg@jj_)v1q$(muuggXFDlxAlh)|5nrzUcGga$qGzSAlxLZu zS48wxBVqA^o00jBW(+c2(LP_MSs3IZ1xa_2L|{UK)(h1|M&J7Wsc-0Kp_NXzWE~AR zQM!V31vV3t_^(J=|N0LZ5iKTAL@_-PEvacf@Nq^eH{hDP%;Q8c={%OeNrv0{S}H%e zd}v`%qc)JT2q)pFLb7&Vj8n6X)23a!44!ZbutX!H6QBi0mg|O-2&C$0HPzBS?Nc*Y z218i+$!0=E-KdBydz(&%6D^q6Vfk61(W$uvoiOmxcY^u^Pcw1t!#14k-BCf9654Fh z(;VcWOpH#vIs(PKfxgge<0N=`uXX}C?b8bdPc<1gPfOrL^?C`5a3W6KL$lHF!C}@5 zD)a-=xLbe|QB9kwne)7&}{Y&p%883gwZh|6Pz&XYA9&iIEkw=?*ONC5lt>zm!zCE z1+4%z>h<(sANaE%sRq|Tj$k-t8cEj&P8#V7I%OL;sjP6S&*OA#f5B)4>*#b%rz5n0=`VZ!TQF!jz7Y|mSeeMsDSdzF{suxO|-*x0nW1achpckOWw7q%* zaKXm(Yl*YtQ;%)jxaqZ*UwUQ)5Pv2x$G-e!!E}G|_+wwj+gBa&Yiw{}-Src5#k(e_ z!3k6P1{_8bh=iOS!>PpnSDrgn>>v3v-1?;s9yvnFyqV*pk3W85wx0MBey&=DT)LUg zZ`gh8Y-2!jk9sD`ZId4Qz^OTX1e_+mv0=leBM0_>XB}|9Aza+^whZmQ{p90+!d}Zl z6*_I(xOwNvsodMgCTCmh8-z|0Hy8wLoEt1P6GsoeaHhY1{nt|{nqPYPWwEAH28%!6 z`qsBkohr}#LABA$6f%igfB&}c-FLFO9+kU^8%A@`>n-M5&AFqAvuDSj*syWSfrERW zUB`0m*V$<3z8ul%nI|6oN+X#m>D{d#TDRrQZ1J7rGnE#DG9l5+Bxm%A>w8_|wO5}X z>m3;RIy-GrFTL`-`tMl4Y46@cuO0c;)4x}(H*&a4sb`BD-vyl(ygTi0T-R4x@Iaq8YjvRRD_`vY` zN8#8x`lUVrGR)-F`FHok6Mueas#*=ClS2q2H~8Q z@dKDlR3BNtZvCkjpL4cya7s7%U`kDXqjj#UOC&%AkXqF|!q;&-MF?NT( zXKSxZ9{XzIH$L~NcLV3^F<4ex?AdPr!|y-(*Y>7y+xlGb^w$%=@|l12 zPRczNE4w64Pd&BwgZF>!$U|Rv`BeSQ`6OG)X&}vSB(wGRY^|sNpP&8cqeOjg49>E* zzw+QWANp61p8a57V^(}GEy-&FPWqOOy^ZXtM^e9a-!FWS!haG2up~}jf9x;5=hya} z{42<{2;4FLlsb7EePe1pq$@ld%=X9iA z8mFD^<+-~1vFnE#>&s83e*TmH=RFjDuoFOMKlrsjeB}G~yikAS5B8mH?F6R=I2Cz= zFIJL`PrRj=`p^II$G-Xa<7dCS1J0uMee;{ofAs!GPXEDweC0&_&OGxwa%lTkB9*Pb zduuKIZ~oz5{V%}yXa^j7?8Hft|NQe0zxr>Vo!YrGWowfu_06O7gAeZLNmTZK$^F)U z_}uMei{_ICW)V&ndjhfVtI#jWX^fN_0)HgN+>?^|)1`zBoK z4ixb_-)nm|Ow=A7eL!_D=n#Sxa9TIimmdbFw;E14HroOVd~OD>94?{nyz7HjpX$E* z?z;^?;iwRBa-+6Q(e;*c$=kt6ZM%WrHgLK{IAK`U)5F{`691RM2|bFZQ=z9=7{E>L zdj_(J>jom6fVHD1ljter+;_kK{e}nmlXu_!fbP}4b`Y++M?IbJXqvvwaLO4@+ktZ< zZ5xD6{hm%Xe;a*ZNjfE;doFr+!$XBZ=<+>-v=4gkis*Db@QQ3ye%Jr-fq)ZqGMwW0 z_;&}sibM)Y-5SG5bh@iQ=lY;ip=dL}PMi!@=m+85Z5UZ!7=%u5k8rvkoVFmvycL?~ zF{RnM^S$6SD!Rt$q;FDqch)cSh`@sL-XZl%=nX!?NxvTErf+}myR8pA+*;T#1t;J{ z9ig9;dWxB(aMHV@e_G$X6`cC;lG@j2lWc9pabNht7bNv9=$nea5`#7VN<_R9XI^OY z;fEiVkbs}-hAGY~$n80%aLi0h0U-^mtkBCCvM^(Ppn?cmW2pjAV89>Io7X!#hgm=2k$dlL@*y<@^&Em= z#o~F@zHe${vtar5`M%k-Wenf9KlZVYEuwjVd8J+3(#4gA_)7T79deeR{4e>RreGS} zViT(hmQDFH27~ER1WV7}qk|t9tAa)*xmNM80#5$fpv-sA717gpTndihcVKi*SgB+R zo1YGx0Iqhb4h@iVwT(Or(nVi+XbHFlI(6W*B*;^{cY9dKDsh|wEOVa>AjW1qET})0 zVDa;C;_puI+c}pj-_8(j)SnC&8Xa)bRdiZQP~BQg2z^Oc4@Zq#8Q3R3`N=lUvJVTm z1bBLuLf-SuwP1E-h~|8>S`%3Q?whH*4N@fYip?UO#kbp87Y@$%SM!Qc z;IcB~+=4TjyHCELZ_-J1N zq#WuU08XDyupzJd=UBDrHh|NhzSye{4L3!Z7lE~twn(`x9)$g+y9sY{s~n70p_JLL zPJ5gLN84W}!Lz@IlXuoYCoS7gl^bp^IBn+L)oG9R6mr0l`%BPGOlY;CjAH*Fa7v;R z8^y`zs0eAsV~1f$kC@Y1<^0_%B1JZ77_C zlO!yyF5HN|Zw*Z|;Br*?3a~b%*;xS1^|3g?4vL+$Pxcd6W4Njq*1MxO#3?YTyLR-u zO~n?gU%rD&bkAgRZYV$HPS0rpW_4lnP%-(}ZeeQ*wgh3Q)aYp&SdTt9yE6F#PUymt zZ{6ANHW%uAVeJ!W(-g;g__}^JA(q=);#Nd#`?`|5=avkHXWqU+1%%Uj*1V7HT^XlP zfou})+}`JU(>0f;x6|is043SCI8>~f$Iy^>rRxYsaQ)b=gKUA47byRI*C{K*I8HpW z&r~?)1aWtiiY_cEoQBX0+MypBc>EHnjN8Z-OAXT1hRwsp)ZK4le$Va_<_#&h;htlQ^AZ-CAI*ed-4rA>J!vrs`V*v@0X)>U;MU(VpFgWdU25 z&0C5tTkJ@Sis55> zrOR9??6wSjB|NBtQzqAggeNG#r}$$TsrMGZrX%F+MuVCaZxNu**RTsXK^|=5wfm$9Ug*Csr9@&-wt7= zBhW_h20MFV^>oVPHgxmm%^2L+$tKaD|tku!+lIGfDwa?&(<-l0W z9X?%2WZM#!ItE`v+{O-KM|ZYuQFLyE$EIt|7fF<+)n( zHRyEk3xESW86m4(`%kKE^Yl*BFV`dG_F@^_HsD$Xypxkm=f~JG zZ`P<@>Qmp`g5j4K^VJyE@cOTu=F_NzrZ@!#o?MjJIK%`POZNme#7v)7-xgdPC5rsf zb{qx$)m{`0rnTr(7%UM^ECxh48DcOjDNOyqv?^BoLtjr6sb5EUsC9-DZPFm{Sgr`7 zok3-=Je(gur0Bp|Nr7u1lj%>4POLTt<^YNYR|@OZ2KmFvF?a<_xcS9Agc;F^-|E?n zl>eN-j36cF6Q0RP-{;|?ep*v7I<;|v=9K5q_Lvm7GC0t&HmPm;kP0WYC&RP)fH}!n z8B9Bec5W2-rRdvI6ACA}d$)eD#`d*=UgkGfCBxD{)09h0KwA_P^u1C4PzPDdyTo}1 ztKx#=%c;EYXf~?^RR%%e?5y*Yr#px5UnNjnfDb+t9`=peCTBFj-vLxF*u=VVrQ!ha@``yMek0y z2oK?CIIR&Wl@m^aCx&CN_$zX+g(a)*fCFHRyQqKp*Jd%RYaF@cYQ|tu3?neGiTWmW zT&>}%WLRiSTGK4;j&t~0&^bsNNu#qVu>nPTkd@S=;e}VLt=9N#9X*Jb+tjh}2hkPwzV|31#rNrC%ZPBw7)XYNi@-*7M+qA zOaXEYV`i(v2pt;B{lcjU-8Jlllj3sFNk*5e4QvKQ=jGoWdNt4y%c4AUoq?j9CF*mu zx=`hvjk8wL#Gzoo2^EeII+2t_IX`jq%VR+R7>cPnBJ`>$Zf;)T+>Zh zzeKhL2kV!0rodU+wn{jmuCn?8_8uu5I|xqb2fgUTBn@7T;VNTTyX_qfvfcfiZ_2q_ zHglwBWc#QWn9mhjGiN3ouFh8-uG?Hp4XneH=^X9>tlN8$zK87{U)#Hy)vw)F*Kltx z^^P|UxSKc0-JOCY5SVC|6*)AeC(g7jxGnWue{R@TN^IGRH_Q&4Fr9G!{ora|>23qx z%L>z9zNO#oTJNQ-fKvvXYBN=L8b-a^uw!Gv^$r)@+;pg)uRUW%=l=co*F?M3g&vT4 z+bwx_(@a6@5#m@zC#_sI=W6cg-{k z`QKhcc+-v^w{447i&ej8P*Q<_t~>SZ)vtoCp~_31HT14aj>yG8VE1khrBuo^cWvu+ z>&xsHi#{~k#!$rl#-SHhw{)!jtuNNb9Yf8H4D8uCjHRL=kW!qdzp{$2{cc~VcOc)| zw5jCQ&6Nb!f(CRB+n&k8b@%dj_C>KvU)qI8-n4zVm1R%dTnA1#3vf?~)zL~{~n0Vke4=rf+ZqwVFHm!mH;75KG;#WuUbQoMz^1f2E~}<|X*&w;x{x zo!o9KQ|iyPhDL_4lk!tRMXGI>^|`rIX?Oayw!;X^-}z^E6rlrjuisp7LvtL!8^x(@ z1^VUjeTaewj&~Ma_WAH*kGaja{S}vFUtw;eM2=Oy)dck!$%pgrP11U38;NTOTgm$Zb+-`8 zsxg|YZ`ReX)bOh?TvZG!ydTDg?Us=4(K8uRxcAQ?*SW8H-Rg#P?Fd#Ik`**qtLvwh zpYCXIq^1bPREOYbaQCYAL#X%WXW>9z2N zK%-E-G!}x{7AMb*NIUf~cY>jE3)-h=Qq~Hu4fry@5&gR1928(RQ2jF&)J=ToNC?r( zU)NkRJ>Mf%%6ak9C1JI{d~L9o0+V*-Z^+G6DPeJr zYD=3zP0 zELRS!$cY@ngXxSug zXL*i9`f-4qB+G1TF+84eNhvGry`1JaF|aRzQ()reH>4y=+3X^ekqd+Ca4^N!D_@ID6Xa?Ml6PcYd}nH8Lzr%MX|`kn4X>vdHtN8ZR8e`cTYVMc7p_ z2dk-ud^(31Ic~wX8#+Ey!v9=%aHM-J2A8$KWT39zO-8t)sUY)0>(7sV`JH)16%%cu z`#CObXrAemN8NQW`85wynIWUcrqcGy>#M8hmFmfSQLW)q-gL^J46i|=iw_e%2Nb1N y`tr2dPMN5;HT8g6VND9(44H=X&46b9T9r3_=;mK0$iepj0000 Date: Mon, 26 Mar 2018 16:45:07 +0200 Subject: [PATCH 19/87] Bring README up to date. --- README.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a851bc4..25433c2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,20 @@ # rk65c02 -65C02 code interpreter/emulator/disassembler (work in progress) +65C02 code interpreter/emulator/disassembler. -![rk65c02 logo](http://c0ff33.net/drop/rk65c02_small.png) +![rk65c02 logo](https://raw.githubusercontent.com/rkujawa/rk65c02/master/res/rk65c02_small.png) + +This project is a work in progress. The rk65c02 is a library implementing an +emulator of WDC 65C02S CPU. It does not aim to be cycle-exact emulator, but +otherwise it tries to mimic behaviour of 65C02S as close as possible. +Currently, the following features are implemented: +- Emulation of all opcodes, including WDC extensions and BCD mode. +- 16-bit address space. +- Support for interrupts. + +The only external dependencies (besides standard C library) are Boehm GC and +uthash. +On Fedora these can be installed with `gc-devel` and `uthash-devel` packages. + +If you want to build tests, `kyua` quality assurance toolkit, `atf` testing +framework and `vasm` assembler are also necessary. From e72871a5b69f6021f89283d0d80b3ca5353508f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Tue, 27 Mar 2018 12:53:08 +0200 Subject: [PATCH 20/87] Use clock_gettime instead of gettimeofday. --- src/log.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/log.c b/src/log.c index f8b08ee..c966930 100644 --- a/src/log.c +++ b/src/log.c @@ -1,7 +1,6 @@ #include #include - -#include +#include #include "log.h" @@ -24,14 +23,14 @@ void rk65c02_loglevel_set(uint8_t l) void rk65c02_log(uint8_t l, const char* fmt, ...) { va_list args; - struct timeval t; - - gettimeofday(&t, NULL); + struct timespec t; if (l > level) return; - fprintf(stderr, "%ld %s:\t", (t.tv_sec * 1000000 + t.tv_usec), + clock_gettime(CLOCK_REALTIME, &t); + + fprintf(stderr, "%ld.%ld %s:\t", t.tv_sec, t.tv_nsec, level_str[l]); va_start(args, fmt); From 25e62f4be4b86975602d9b0c5d510f4547c1c8e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Tue, 27 Mar 2018 13:18:29 +0200 Subject: [PATCH 21/87] Avoid compiler warning (variable uninitialized). --- src/bus.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bus.c b/src/bus.c index a0dfcde..4cbf951 100644 --- a/src/bus.c +++ b/src/bus.c @@ -109,6 +109,8 @@ bus_write_1(bus_t *t, uint16_t addr, uint8_t val) uint16_t off; device_t *d; + off = 0; + bus_access_device(t, addr, &d, &off); if (t->access_debug) From 9ff0cb26fcdaccfcddd3a7f709709f11eec8e1e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Tue, 27 Mar 2018 13:30:33 +0200 Subject: [PATCH 22/87] Explicitely cast time to long long ints. Avoid portability bullshit problems. --- src/log.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/log.c b/src/log.c index c966930..6977497 100644 --- a/src/log.c +++ b/src/log.c @@ -30,8 +30,8 @@ void rk65c02_log(uint8_t l, const char* fmt, ...) clock_gettime(CLOCK_REALTIME, &t); - fprintf(stderr, "%ld.%ld %s:\t", t.tv_sec, t.tv_nsec, - level_str[l]); + fprintf(stderr, "%lld.%lld %s:\t", (long long int) t.tv_sec, + (long long int) t.tv_nsec, level_str[l]); va_start(args, fmt); vfprintf(stderr, fmt, args); From 0b2be69481e964d3f967bc28c8af97866d3aad08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Wed, 28 Mar 2018 13:18:44 +0200 Subject: [PATCH 23/87] Add test case for ASL. --- test/test_emulation.c | 29 +++++++++++++++++++++++++++++ test/test_emulation_asl.s | 7 +++++++ 2 files changed, 36 insertions(+) create mode 100644 test/test_emulation_asl.s diff --git a/test/test_emulation.c b/test/test_emulation.c index 2e317f7..8005df4 100644 --- a/test/test_emulation.c +++ b/test/test_emulation.c @@ -542,6 +542,34 @@ ATF_TC_BODY(emul_and, tc) bus_finish(&b); } +ATF_TC_WITHOUT_HEAD(emul_asl); +ATF_TC_BODY(emul_asl, tc) +{ + rk65c02emu_t e; + bus_t b; + + b = bus_init_with_default_devs(); + e = rk65c02_init(&b); + + e.regs.A = 0xAA; + e.regs.X = 0x1; + bus_write_1(&b, 0x10, 0xAA); + bus_write_1(&b, 0x11, 0xAA); + bus_write_1(&b, 0x300, 0xFF); + bus_write_1(&b, 0x301, 0xFF); + + ATF_REQUIRE(rom_start(&e, "test_emulation_asl.rom", tc)); + + ATF_CHECK(e.regs.A == 0x54); + ATF_CHECK(bus_read_1(&b, 0x10) == 0x54); + ATF_CHECK(bus_read_1(&b, 0x11) == 0x54); + ATF_CHECK(bus_read_1(&b, 0x300) == 0xFE); + ATF_CHECK(bus_read_1(&b, 0x301) == 0xFE); + ATF_CHECK(e.regs.P & P_CARRY); + + bus_finish(&b); +} + ATF_TC_WITHOUT_HEAD(emul_nop); ATF_TC_BODY(emul_nop, tc) { @@ -1351,6 +1379,7 @@ ATF_TC_BODY(emul_smb, tc) ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, emul_and); + ATF_TP_ADD_TC(tp, emul_asl); ATF_TP_ADD_TC(tp, emul_adc_16bit); ATF_TP_ADD_TC(tp, emul_adc_bcd); ATF_TP_ADD_TC(tp, emul_bit); diff --git a/test/test_emulation_asl.s b/test/test_emulation_asl.s new file mode 100644 index 0000000..87877b6 --- /dev/null +++ b/test/test_emulation_asl.s @@ -0,0 +1,7 @@ +start: asl A + asl 0x10 + asl 0x10,X + asl 0x300 + asl 0x300,X + stp + From 260ac09c01a8390fcd16a721b70d752b2791ea94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Wed, 28 Mar 2018 13:46:06 +0200 Subject: [PATCH 24/87] Emulate zero page wrap for zp,x addressing. --- src/instruction.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/instruction.c b/src/instruction.c index 8554062..4ee12da 100644 --- a/src/instruction.c +++ b/src/instruction.c @@ -283,8 +283,7 @@ instruction_data_write_1(rk65c02emu_t *e, instrdef_t *id, instruction_t *i, uint bus_write_1(e->bus, i->op1, val); break; case ZPX: - /* XXX: wraps around zero page? */ - bus_write_1(e->bus, i->op1 + e->regs.X, val); + bus_write_1(e->bus, (uint8_t) (i->op1 + e->regs.X), val); break; case ZPY: bus_write_1(e->bus, i->op1 + e->regs.Y, val); From 390bbdc14eeca1d28501c9f321bf37479b4a0e8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Wed, 28 Mar 2018 13:47:18 +0200 Subject: [PATCH 25/87] Add test cases for LSR and zero page wrap. --- test/test_emulation.c | 51 ++++++++++++++++++++++++++++++++++ test/test_emulation_lsr.s | 7 +++++ test/test_emulation_wrap_zpx.s | 7 +++++ 3 files changed, 65 insertions(+) create mode 100644 test/test_emulation_lsr.s create mode 100644 test/test_emulation_wrap_zpx.s diff --git a/test/test_emulation.c b/test/test_emulation.c index 8005df4..bc9d381 100644 --- a/test/test_emulation.c +++ b/test/test_emulation.c @@ -570,6 +570,34 @@ ATF_TC_BODY(emul_asl, tc) bus_finish(&b); } +ATF_TC_WITHOUT_HEAD(emul_lsr); +ATF_TC_BODY(emul_lsr, tc) +{ + rk65c02emu_t e; + bus_t b; + + b = bus_init_with_default_devs(); + e = rk65c02_init(&b); + + e.regs.A = 0x55; + e.regs.X = 0x1; + bus_write_1(&b, 0x10, 0x55); + bus_write_1(&b, 0x11, 0x55); + bus_write_1(&b, 0x300, 0xFF); + bus_write_1(&b, 0x301, 0xFF); + + ATF_REQUIRE(rom_start(&e, "test_emulation_lsr.rom", tc)); + + ATF_CHECK(e.regs.A == 0x2A); + ATF_CHECK(bus_read_1(&b, 0x10) == 0x2A); + ATF_CHECK(bus_read_1(&b, 0x11) == 0x2A); + ATF_CHECK(bus_read_1(&b, 0x300) == 0x7F); + ATF_CHECK(bus_read_1(&b, 0x301) == 0x7F); + ATF_CHECK(e.regs.P & P_CARRY); + + bus_finish(&b); +} + ATF_TC_WITHOUT_HEAD(emul_nop); ATF_TC_BODY(emul_nop, tc) { @@ -1376,6 +1404,26 @@ ATF_TC_BODY(emul_smb, tc) } } +ATF_TC_WITHOUT_HEAD(emul_wrap_zpx); +ATF_TC_BODY(emul_wrap_zpx, tc) +{ + rk65c02emu_t e; + bus_t b; + + b = bus_init_with_default_devs(); + e = rk65c02_init(&b); + + e.regs.A = 0xAA; + e.regs.X = 0x7F; + ATF_REQUIRE(rom_start(&e, "test_emulation_wrap_zpx.rom", tc)); + + ATF_CHECK(bus_read_1(&b, 0x8F) == 0xAA); + ATF_CHECK(bus_read_1(&b, 0xFF) == 0xAA); + ATF_CHECK(bus_read_1(&b, 0x00) == 0xAA); + ATF_CHECK(bus_read_1(&b, 0x01) == 0xAA); + ATF_CHECK(bus_read_1(&b, 0x7E) == 0xAA); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, emul_and); @@ -1397,6 +1445,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, emul_jmp); ATF_TP_ADD_TC(tp, emul_jsr_rts); ATF_TP_ADD_TC(tp, emul_lda); + ATF_TP_ADD_TC(tp, emul_lsr); ATF_TP_ADD_TC(tp, emul_nop); ATF_TP_ADD_TC(tp, emul_ora); ATF_TP_ADD_TC(tp, emul_stz); @@ -1414,6 +1463,8 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, emul_sign_overflow_basic); ATF_TP_ADD_TC(tp, emul_sign_overflow_thorough); + ATF_TP_ADD_TC(tp, emul_wrap_zpx); + return (atf_no_error()); } diff --git a/test/test_emulation_lsr.s b/test/test_emulation_lsr.s new file mode 100644 index 0000000..0162646 --- /dev/null +++ b/test/test_emulation_lsr.s @@ -0,0 +1,7 @@ +start: lsr A + lsr 0x10 + lsr 0x10,X + lsr 0x300 + lsr 0x300,X + stp + diff --git a/test/test_emulation_wrap_zpx.s b/test/test_emulation_wrap_zpx.s new file mode 100644 index 0000000..17f5db2 --- /dev/null +++ b/test/test_emulation_wrap_zpx.s @@ -0,0 +1,7 @@ +start: sta 0x10,X + sta 0x80,X + sta 0x81,X + sta 0x82,X + sta 0xFF,X + stp + From b35323cb3db13bcc9420572eb80d8fea2f93df4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Wed, 28 Mar 2018 13:52:07 +0200 Subject: [PATCH 26/87] Use logging function instead of printf. --- test/utils.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/utils.c b/test/utils.c index 7eaf446..c6f5e67 100644 --- a/test/utils.c +++ b/test/utils.c @@ -8,6 +8,7 @@ #include "bus.h" #include "rk65c02.h" +#include "log.h" #include "utils.h" @@ -33,7 +34,7 @@ rom_start(rk65c02emu_t *e, const char *name, const atf_tc_t *tc) const char *path; path = rom_path(name, tc); - printf("%s\n", path); + rk65c02_log(LOG_INFO, "Loading ROM: %s", path); e->regs.PC = ROM_LOAD_ADDR; if(!bus_load_file(e->bus, ROM_LOAD_ADDR, path)) return false; From 4f299a6f3640b7f485b15158ac728a574763e5b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Wed, 28 Mar 2018 14:18:50 +0200 Subject: [PATCH 27/87] Also fix zero page wrap for bus reads. --- src/instruction.c | 3 +-- test/test_emulation.c | 8 ++++++++ test/test_emulation_wrap_zpx.s | 11 +++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/instruction.c b/src/instruction.c index 4ee12da..350a730 100644 --- a/src/instruction.c +++ b/src/instruction.c @@ -353,8 +353,7 @@ instruction_data_read_1(rk65c02emu_t *e, instrdef_t *id, instruction_t *i) rv = bus_read_1(e->bus, i->op1); break; case ZPX: - /* XXX: wraps around zero page? */ - rv = bus_read_1(e->bus, i->op1 + e->regs.X); + rv = bus_read_1(e->bus, (uint8_t) (i->op1 + e->regs.X)); break; case ZPY: rv = bus_read_1(e->bus, i->op1 + e->regs.Y); diff --git a/test/test_emulation.c b/test/test_emulation.c index bc9d381..34d1fa5 100644 --- a/test/test_emulation.c +++ b/test/test_emulation.c @@ -1409,6 +1409,7 @@ ATF_TC_BODY(emul_wrap_zpx, tc) { rk65c02emu_t e; bus_t b; + uint16_t i; b = bus_init_with_default_devs(); e = rk65c02_init(&b); @@ -1422,6 +1423,13 @@ ATF_TC_BODY(emul_wrap_zpx, tc) ATF_CHECK(bus_read_1(&b, 0x00) == 0xAA); ATF_CHECK(bus_read_1(&b, 0x01) == 0xAA); ATF_CHECK(bus_read_1(&b, 0x7E) == 0xAA); + + i = 0x200; + + while (i < 0x205) { + ATF_CHECK(bus_read_1(&b, i) == 0xAA); + i++; + } } ATF_TP_ADD_TCS(tp) diff --git a/test/test_emulation_wrap_zpx.s b/test/test_emulation_wrap_zpx.s index 17f5db2..8c0bf9d 100644 --- a/test/test_emulation_wrap_zpx.s +++ b/test/test_emulation_wrap_zpx.s @@ -3,5 +3,16 @@ start: sta 0x10,X sta 0x81,X sta 0x82,X sta 0xFF,X + + ldy 0x10,X + sty 0x200 + ldy 0x80,X + sty 0x201 + ldy 0x81,X + sty 0x202 + ldy 0x82,X + sty 0x203 + ldy 0xFF,X + sty 0x204 stp From 7c4d6cea3f8e6be2b11cadba47a20b0a40c837a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Sat, 31 Mar 2018 12:38:45 +0200 Subject: [PATCH 28/87] Fix indirect indexed with Y addressing. Also fix test case for this. Problem wasn't detected, because test case was broken too. --- src/instruction.c | 10 ++++------ test/test_emulation.c | 8 ++++---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/instruction.c b/src/instruction.c index 350a730..6845f54 100644 --- a/src/instruction.c +++ b/src/instruction.c @@ -302,11 +302,10 @@ instruction_data_write_1(rk65c02emu_t *e, instrdef_t *id, instruction_t *i, uint iaddr |= (bus_read_1(e->bus, i->op1 + e->regs.X + 1) << 8); bus_write_1(e->bus, iaddr, val); break; - case IZPY: - /* XXX */ + case IZPY: /* Zero Page Indirect Indexed with Y */ iaddr = bus_read_1(e->bus, i->op1); iaddr |= (bus_read_1(e->bus, i->op1 + 1) << 8); - bus_write_1(e->bus, iaddr, val + e->regs.Y); + bus_write_1(e->bus, iaddr + e->regs.Y, val); break; case ABSOLUTEX: bus_write_1(e->bus, (i->op1 + (i->op2 << 8)) + e->regs.X, val); @@ -369,11 +368,10 @@ instruction_data_read_1(rk65c02emu_t *e, instrdef_t *id, instruction_t *i) iaddr |= (bus_read_1(e->bus, i->op1 + e->regs.X + 1) << 8); rv = bus_read_1(e->bus, iaddr); break; - case IZPY: - /* XXX: what about page wraps / roll over */ + case IZPY: /* Zero Page Indirect Indexed with Y */ iaddr = bus_read_1(e->bus, i->op1); iaddr |= (bus_read_1(e->bus, i->op1 + 1) << 8); - rv = bus_read_1(e->bus, iaddr) + e->regs.Y; + rv = bus_read_1(e->bus, iaddr + e->regs.Y); break; case ABSOLUTE: rv = bus_read_1(e->bus, i->op1 + (i->op2 << 8)); diff --git a/test/test_emulation.c b/test/test_emulation.c index 34d1fa5..65d97c8 100644 --- a/test/test_emulation.c +++ b/test/test_emulation.c @@ -122,7 +122,7 @@ ATF_TC_BODY(emul_cmp, tc) e.regs.Y = 0x01; bus_write_1(&b, 0x22, 0x0); bus_write_1(&b, 0x23, 0x20); - bus_write_1(&b, 0x2000, 0xF); + bus_write_1(&b, 0x2001, 0x10); rk65c02_dump_regs(e.regs); ATF_REQUIRE(rom_start(&e, "test_emulation_cmp_izpy.rom", tc)); rk65c02_dump_regs(e.regs); @@ -662,11 +662,11 @@ ATF_TC_BODY(emul_sta, tc) ATF_REQUIRE(rom_start(&e, "test_emulation_sta_izpx.rom", tc)); ATF_CHECK(bus_read_1(&b, 0x2010) == 0xAA); /* STA indirect zero page Y */ - e.regs.A = 0x54; + e.regs.A = 0x55; e.regs.X = 0; e.regs.Y = 0x1; ATF_REQUIRE(rom_start(&e, "test_emulation_sta_izpy.rom", tc)); - ATF_CHECK(bus_read_1(&b, 0x2010) == 0x55); + ATF_CHECK(bus_read_1(&b, 0x2011) == 0x55); bus_finish(&b); } @@ -729,7 +729,7 @@ ATF_TC_BODY(emul_ora, tc) e.regs.A = 0xAA; e.regs.X = 0; e.regs.Y = 0x1; - bus_write_1(&b, 0x2A04, 0x54); + bus_write_1(&b, 0x2A05, 0x55); bus_write_1(&b, 0x14, 0x04); bus_write_1(&b, 0x15, 0x2A); ATF_REQUIRE(rom_start(&e, "test_emulation_ora_izpy.rom", tc)); From 3c684fbf2c9132610656e30f425300bbe4f5da9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Sat, 31 Mar 2018 15:03:22 +0200 Subject: [PATCH 29/87] Fix zero page wrap for indexed indirect mode. --- src/instruction.c | 14 ++++++-------- test/test_emulation.c | 29 +++++++++++++++++++++++++++++ test/test_emulation_wrap_izpx.s | 7 +++++++ 3 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 test/test_emulation_wrap_izpx.s diff --git a/src/instruction.c b/src/instruction.c index 6845f54..2333349 100644 --- a/src/instruction.c +++ b/src/instruction.c @@ -296,10 +296,9 @@ instruction_data_write_1(rk65c02emu_t *e, instrdef_t *id, instruction_t *i, uint case ABSOLUTE: bus_write_1(e->bus, i->op1 + (i->op2 << 8), val); break; - case IZPX: - /* XXX */ - iaddr = bus_read_1(e->bus, i->op1 + e->regs.X); - iaddr |= (bus_read_1(e->bus, i->op1 + e->regs.X + 1) << 8); + case IZPX: /* Zero Page Indexed Indirect with X */ + iaddr = bus_read_1(e->bus,(uint8_t) (i->op1 + e->regs.X)); + iaddr |= (bus_read_1(e->bus, (uint8_t) (i->op1 + e->regs.X + 1)) << 8); bus_write_1(e->bus, iaddr, val); break; case IZPY: /* Zero Page Indirect Indexed with Y */ @@ -362,10 +361,9 @@ instruction_data_read_1(rk65c02emu_t *e, instrdef_t *id, instruction_t *i) iaddr |= (bus_read_1(e->bus, i->op1 + 1) << 8); rv = bus_read_1(e->bus, iaddr); break; - case IZPX: - /* XXX: what about page wraps / roll over */ - iaddr = bus_read_1(e->bus, i->op1 + e->regs.X); - iaddr |= (bus_read_1(e->bus, i->op1 + e->regs.X + 1) << 8); + case IZPX: /* Zero Page Indexed Indirect with X */ + iaddr = bus_read_1(e->bus, (uint8_t) (i->op1 + e->regs.X)); + iaddr |= (bus_read_1(e->bus, (uint8_t) (i->op1 + e->regs.X + 1)) << 8); rv = bus_read_1(e->bus, iaddr); break; case IZPY: /* Zero Page Indirect Indexed with Y */ diff --git a/test/test_emulation.c b/test/test_emulation.c index 65d97c8..b4b8438 100644 --- a/test/test_emulation.c +++ b/test/test_emulation.c @@ -8,6 +8,7 @@ #include "rk65c02.h" #include "instruction.h" #include "debug.h" +#include "log.h" #include "utils.h" ATF_TC_WITHOUT_HEAD(emul_bit); @@ -1404,6 +1405,33 @@ ATF_TC_BODY(emul_smb, tc) } } +ATF_TC_WITHOUT_HEAD(emul_wrap_izpx); +ATF_TC_BODY(emul_wrap_izpx, tc) +{ + rk65c02emu_t e; + bus_t b; + + b = bus_init_with_default_devs(); + e = rk65c02_init(&b); + + e.regs.A = 0xAA; + e.regs.X = 0xA0; + + bus_write_1(&b, 0xB0, 0x10); + bus_write_1(&b, 0xB1, 0x20); + bus_write_1(&b, 0x90, 0x11); + bus_write_1(&b, 0x91, 0x20); + + bus_write_1(&b, 0x2011, 0x55); + + rk65c02_dump_regs(e.regs); + ATF_REQUIRE(rom_start(&e, "test_emulation_wrap_izpx.rom", tc)); + rk65c02_dump_regs(e.regs); + + ATF_CHECK(bus_read_1(&b, 0x2010) == 0xAA); + ATF_CHECK(e.regs.A == 0x55); +} + ATF_TC_WITHOUT_HEAD(emul_wrap_zpx); ATF_TC_BODY(emul_wrap_zpx, tc) { @@ -1472,6 +1500,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, emul_sign_overflow_thorough); ATF_TP_ADD_TC(tp, emul_wrap_zpx); + ATF_TP_ADD_TC(tp, emul_wrap_izpx); return (atf_no_error()); } diff --git a/test/test_emulation_wrap_izpx.s b/test/test_emulation_wrap_izpx.s new file mode 100644 index 0000000..1742893 --- /dev/null +++ b/test/test_emulation_wrap_izpx.s @@ -0,0 +1,7 @@ +start: sta (0x10,X) +; lda (0xF0,X) +.byte 0xA1,0xF0 + + stp + + From 68fa918d97403fa5566857071d372b2297d15a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Sun, 1 Apr 2018 21:16:58 +0200 Subject: [PATCH 30/87] Document the way invalid opcodes are handled. --- src/rk65c02.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/rk65c02.c b/src/rk65c02.c index c7381ee..49a2df5 100644 --- a/src/rk65c02.c +++ b/src/rk65c02.c @@ -123,8 +123,15 @@ rk65c02_exec(rk65c02emu_t *e) if (!instruction_modify_pc(&id)) program_counter_increment(e, &id); } else { - rk65c02_log(LOG_ERROR, "unimplemented opcode %X @ %X\n", + /* + * Technically, on a real 65C02, all invalid opcodes + * are NOPs, but until rk65c02 reaches some level of + * maturity, let's catch them here to help iron out the + * bugs. + */ + rk65c02_log(LOG_WARN, "invalid opcode %X @ %X\n", i.opcode, e->regs.PC); + e->state = STOPPED; e->stopreason = EMUERROR; } From 7cf3f263e3214d340c909ecc4f978fd56471e85a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Sun, 1 Apr 2018 21:40:49 +0200 Subject: [PATCH 31/87] Simplify handling ciritcal emulation errors. --- src/instruction.c | 8 ++++---- src/rk65c02.c | 22 ++++++++++++++++++---- src/rk65c02.h | 3 ++- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/instruction.c b/src/instruction.c index 2333349..3de98ff 100644 --- a/src/instruction.c +++ b/src/instruction.c @@ -325,8 +325,8 @@ instruction_data_write_1(rk65c02emu_t *e, instrdef_t *id, instruction_t *i, uint * PC which is handled within emulation of a given opcode. */ default: - rk65c02_log(LOG_ERROR, - "unhandled addressing mode for opcode %x\n", i->opcode); + rk65c02_panic(e, "unhandled addressing mode for opcode %x\n", + i->opcode); break; } } @@ -389,8 +389,8 @@ instruction_data_read_1(rk65c02emu_t *e, instrdef_t *id, instruction_t *i) * PC which is handled within emulation of a given opcode. */ default: - rk65c02_log(LOG_ERROR, - "unhandled addressing mode for opcode %x\n", i->opcode); + rk65c02_panic(e, "unhandled addressing mode for opcode %x\n", + i->opcode); break; } diff --git a/src/rk65c02.c b/src/rk65c02.c index 49a2df5..ade5ae2 100644 --- a/src/rk65c02.c +++ b/src/rk65c02.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -129,11 +130,8 @@ rk65c02_exec(rk65c02emu_t *e) * maturity, let's catch them here to help iron out the * bugs. */ - rk65c02_log(LOG_WARN, "invalid opcode %X @ %X\n", + rk65c02_panic(e, "invalid opcode %X @ %X\n", i.opcode, e->regs.PC); - - e->state = STOPPED; - e->stopreason = EMUERROR; } if (e->trace) @@ -235,6 +233,22 @@ rk65c02_regs_string_get(reg_state_t regs) return str; } + +void +rk65c02_panic(rk65c02emu_t *e, const char* fmt, ...) +{ + va_list args; + + va_start(args, fmt); + rk65c02_log(LOG_CRIT, fmt, args); + va_end(args); + + e->state = STOPPED; + e->stopreason = EMUERROR; + + /* TODO: run some UI callback. */ +} + /* int main(void) diff --git a/src/rk65c02.h b/src/rk65c02.h index 259b11e..c678478 100644 --- a/src/rk65c02.h +++ b/src/rk65c02.h @@ -82,7 +82,8 @@ void rk65c02_step(rk65c02emu_t *, uint16_t); char *rk65c02_regs_string_get(reg_state_t); void rk65c02_dump_regs(reg_state_t); void rk65c02_dump_stack(rk65c02emu_t *, uint8_t); -void rk65c02_irq(rk65c02emu_t *e); +void rk65c02_irq(rk65c02emu_t *); +void rk65c02_panic(rk65c02emu_t *, const char*, ...); #endif From b44dcdc91a59a7d4fd725d9bbfead5d6d544a100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Thu, 5 Apr 2018 00:32:53 +0200 Subject: [PATCH 32/87] Add example routine finding min among 3 numbers. --- examples/min3.s | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 examples/min3.s diff --git a/examples/min3.s b/examples/min3.s new file mode 100644 index 0000000..0b2a9c2 --- /dev/null +++ b/examples/min3.s @@ -0,0 +1,28 @@ +; min3 +; Takes 3 numbers (A, B, C), passed on stack, finds the minimum. +; Result is also passed on stack. Assumes it is being called via jsr. +.set retl,0x10 +.set reth,0x11 +.set res,0x12 +min3: pla ; pull low byte of return address + sta retl + pla + sta reth + pla ; pull C from stack + sta res ; save C into res + pla ; pull B from stack + cmp res ; compare B and C + bpl bltc ; branch if B > C + sta res ; if C is smaller, save it to res +bltc: pla ; pull A from stack + cmp res ; compare A and whatever is in res + bpl ret ; branch if A > res + sta res ; otherwise save A to res +ret: lda res ; load res into accumulator + pha ; save to the stack + lda reth ; restore return address + pha + lda retl + pha + rts ; return from function + From 5173d10a69e037e889bcfe2f92aaee2bcaad421b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Fri, 6 Apr 2018 14:25:26 +0200 Subject: [PATCH 33/87] Add code to actually run the example. --- examples/Makefile | 23 +++++++++++++++++++++++ examples/min3.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 examples/Makefile create mode 100644 examples/min3.c diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..08c8441 --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,23 @@ +CFLAGS=-Wall -pedantic -I../src -g +LDFLAGS=-latf-c -lgc +RK6502LIB=../src/librk65c02.a +VASM=vasm6502_std +VASMFLAGS=-Fbin -c02 + +EXAMPLES=min3 +EXAMPLES_ROMS:=$(addsuffix .rom,$(basename $(wildcard *.s))) + +all : $(EXAMPLES) $(EXAMPLES_ROMS) + +min3 : min3.o $(RK6502LIB) + $(CC) -o $@ $(LDFLAGS) $< $(RK6502LIB) + +%.rom : %.s + $(VASM) $(VASMFLAGS) -o $@ $< + +%.o : %.c + $(CC) $(CFLAGS) -c $< +clean : + rm -f *.o + rm -f $(EXAMPLES) $(EXAMPLES_ROMS) + diff --git a/examples/min3.c b/examples/min3.c new file mode 100644 index 0000000..c36f8e7 --- /dev/null +++ b/examples/min3.c @@ -0,0 +1,35 @@ +#include +#include + +#include "rk65c02.h" +#include "bus.h" +#include "log.h" +#include "instruction.h" + +static const uint16_t load_addr = 0xC000; + +int main(void) +{ + uint8_t a, b, c; + uint8_t min; + bus_t bus; + rk65c02emu_t e; + + bus = bus_init_with_default_devs(); + e = rk65c02_init(&bus); + + bus_load_file(e.bus, load_addr, "min3.rom"); + + e.regs.SP = 0xFF; + e.regs.PC = load_addr; + a = 5; b = 9; c = 4; + + stack_push(&e, a); + stack_push(&e, b); + stack_push(&e, c); + + rk65c02_start(&e); + min = stack_pop(&e); + printf("Min is: %d\n", min); +} + From adef30da21ccd142cb57a93252924467e356dc6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Fri, 6 Apr 2018 14:26:00 +0200 Subject: [PATCH 34/87] Load the example at 0xC000, stop the emulator after running. --- examples/min3.s | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/min3.s b/examples/min3.s index 0b2a9c2..0906e45 100644 --- a/examples/min3.s +++ b/examples/min3.s @@ -1,3 +1,7 @@ +.org 0xC000 +start: jsr min3 + stp + ; min3 ; Takes 3 numbers (A, B, C), passed on stack, finds the minimum. ; Result is also passed on stack. Assumes it is being called via jsr. From eeb564d69fdaaba84997acb64943a685f439f873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Fri, 6 Apr 2018 14:27:05 +0200 Subject: [PATCH 35/87] Make log less verbose. --- src/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bus.c b/src/bus.c index 4cbf951..68ebcd4 100644 --- a/src/bus.c +++ b/src/bus.c @@ -35,7 +35,7 @@ bus_device_add(bus_t *b, device_t *d, uint16_t addr) LL_APPEND((b->dm_head), dm); - rk65c02_log(LOG_INFO, "Bus mapping added: %x device %s size %x.", + rk65c02_log(LOG_DEBUG, "Bus mapping added: %x device %s size %x.", addr, d->name, d->size); } From fe94c747e584568a34c7f5638b7faabe83257830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Mon, 9 Apr 2018 15:00:02 +0200 Subject: [PATCH 36/87] Add test case for signed comparison. --- test/test_emulation.c | 16 ++++ test/test_emulation_signed_comparison.s | 100 ++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 test/test_emulation_signed_comparison.s diff --git a/test/test_emulation.c b/test/test_emulation.c index b4b8438..ddbb617 100644 --- a/test/test_emulation.c +++ b/test/test_emulation.c @@ -1222,6 +1222,20 @@ ATF_TC_BODY(emul_sign_overflow_thorough, tc) } +ATF_TC_WITHOUT_HEAD(emul_signed_comparison); +ATF_TC_BODY(emul_signed_comparison, tc) +{ + rk65c02emu_t e; + bus_t b; + + b = bus_init_with_default_devs(); + e = rk65c02_init(&b); + + ATF_REQUIRE(rom_start(&e, "test_emulation_signed_comparison.rom", tc)); + ATF_CHECK(bus_read_1(&b, 0x13) == 0x0); + +} + ATF_TC_WITHOUT_HEAD(emul_sbc); ATF_TC_BODY(emul_sbc, tc) { @@ -1499,6 +1513,8 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, emul_sign_overflow_basic); ATF_TP_ADD_TC(tp, emul_sign_overflow_thorough); + ATF_TP_ADD_TC(tp, emul_signed_comparison); + ATF_TP_ADD_TC(tp, emul_wrap_zpx); ATF_TP_ADD_TC(tp, emul_wrap_izpx); diff --git a/test/test_emulation_signed_comparison.s b/test/test_emulation_signed_comparison.s new file mode 100644 index 0000000..c96a41e --- /dev/null +++ b/test/test_emulation_signed_comparison.s @@ -0,0 +1,100 @@ +; stolen from: http://www.6502.org/tutorials/compare_beyond.html +; adapted to vasm and std syntax by rkujawa +; +; Test the signed compare routine +; +; Returns with ERROR = 0 if the test passes, ERROR = 1 if the test fails +; +; Three (additional) memory locations are used: ERROR, N1, and N2 +; These may be located anywhere convenient in RAM +; +.org 0xC000 +.set N1, 0x10 +.set N2, 0x11 +.set ERROR, 0x12 +TEST: cld ; Clear decimal mode for test + lda #1 + sta ERROR ; Store 1 in ERROR until test passes + tsx ; Save stack pointer so subroutines can exit with ERROR = 1 +; +; Test N1 positive, N2 positive +; + lda #0 ; 0 + sta N1 +PP1: lda #0 ; 0 + sta N2 +PP2: jsr SUCMP ; Verify that the signed and unsigned comparison agree + inc N2 + bpl PP2 + inc N1 + bpl PP1 +; +; Test N1 positive, N2 negative +; + lda #0 ; 0 + sta N1 +PN1: lda #0x80 ; -128 + sta N2 +PN2: jsr SCMP ; Signed comparison + bmi TEST1 ; if N1 (positive) < N2 (negative) exit with ERROR = 1 + inc N2 + bmi PN2 + inc N1 + bpl PN1 +; +; Test N1 negative, N2 positive +; + lda #0x80 ; -128 + sta N1 +NP1: lda #0 ; 0 + sta N2 +NP2: jsr SCMP ; Signed comparison + bpl TEST1 ; if N1 (negative) >= N2 (positive) exit with ERROR = 1 + inc N2 + bpl NP2 + inc N1 + bmi NP1 +; +; Test N1 negative, N2 negative +; + lda #0x80 ; -128 + sta N1 +NN1: lda #0x80 ; -128 + sta N2 +NN2: jsr SUCMP ; Verify that the signed and unsigned comparisons agree + inc N2 + bmi NN2 + inc N1 + bmi NN1 + + lda #0 + sta ERROR ; All tests pass, so store 0 in ERROR +TEST1: stp + +; Signed comparison +; +; Returns with: +; N=0 (BPL branches) if N1 >= N2 (signed) +; N=1 (BMI branches) if N1 < N2 (signed) +; +; The unsigned comparison result is returned in the C flag (for free) +; +SCMP: sec + lda N1 ; Compare N1 and N2 + sbc N2 + bvc SCMP1 ; Branch if V = 0 + eor #0x80 ; Invert Accumulator bit 7 (which also inverts the N flag) +SCMP1: rts + +; Test the signed and unsigned comparisons to confirm that they agree +; +SUCMP: jsr SCMP ; Signed (and unsigned) comparison + bcc SUCMP2 ; Branch if N1 < N2 (unsigned) + bpl SUCMP1 ; N1 >= N2 (unsigned), branch if N1 >= N2 (signed) + tsx ; reset stack and exit with ERROR = 1 +SUCMP1: rts +SUCMP2: bmi SUCMP3 ; N1 < N2 (unsigned), branch if N1 < N2 (signed) + tsx ; reset stack and exit with ERROR = 1 +SUCMP3: rts + + From 96f955e8c11107ea7b3e2a95f969fcb0b38682e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Mon, 9 Apr 2018 17:07:21 +0200 Subject: [PATCH 37/87] Update year. --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index cbd984f..733d9cd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Radosław Kujawa +Copyright (c) 2017-2018 Radosław Kujawa Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From ed4b2786b6e7576f7e570679ae69640c93e78299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Tue, 10 Apr 2018 10:12:27 +0200 Subject: [PATCH 38/87] Simplify ROM loading by providing utility function. --- examples/min3.c | 8 +++----- src/rk65c02.c | 24 ++++++++++++++++++++++++ src/rk65c02.h | 1 + 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/examples/min3.c b/examples/min3.c index c36f8e7..0f112ba 100644 --- a/examples/min3.c +++ b/examples/min3.c @@ -12,13 +12,10 @@ int main(void) { uint8_t a, b, c; uint8_t min; - bus_t bus; + rk65c02emu_t e; - bus = bus_init_with_default_devs(); - e = rk65c02_init(&bus); - - bus_load_file(e.bus, load_addr, "min3.rom"); + e = rk65c02_load_rom("min3.rom", load_addr, NULL); e.regs.SP = 0xFF; e.regs.PC = load_addr; @@ -29,6 +26,7 @@ int main(void) stack_push(&e, c); rk65c02_start(&e); + min = stack_pop(&e); printf("Min is: %d\n", min); } diff --git a/src/rk65c02.c b/src/rk65c02.c index ade5ae2..a2055f7 100644 --- a/src/rk65c02.c +++ b/src/rk65c02.c @@ -18,6 +18,30 @@ void rk65c02_exec(rk65c02emu_t *); +/** + * \brief Prep the emulator, load code from file, pass bus config optionally. + * \param path Path to ROM file to be loaded. + * \param load_addr Address on the bus where ROM should be loaded. + * \param b Pre-existing bus configuration, pass NULL if default requested. + */ +rk65c02emu_t +rk65c02_load_rom(const char *path, uint16_t load_addr, bus_t *b) +{ + rk65c02emu_t e; + + if (b == NULL) { + b = GC_MALLOC(sizeof(bus_t)); + *b = bus_init_with_default_devs(); + } + + /* XXX: normal error handling instead of assert would be preferred */ + assert(bus_load_file(b, load_addr, path)); + + e = rk65c02_init(b); + + return e; +} + /* * Prepare the emulator for use, set initial CPU state. */ diff --git a/src/rk65c02.h b/src/rk65c02.h index c678478..4d624df 100644 --- a/src/rk65c02.h +++ b/src/rk65c02.h @@ -84,6 +84,7 @@ void rk65c02_dump_regs(reg_state_t); void rk65c02_dump_stack(rk65c02emu_t *, uint8_t); void rk65c02_irq(rk65c02emu_t *); void rk65c02_panic(rk65c02emu_t *, const char*, ...); +rk65c02emu_t rk65c02_load_rom(const char *, uint16_t, bus_t *); #endif From 95368a186a676f0088aa26ea5f6eb53d61fb430a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Tue, 10 Apr 2018 10:23:32 +0200 Subject: [PATCH 39/87] Remove leftover attempt at CLI. --- src/rk65c02cli.c | 63 ------------------------------------------------ 1 file changed, 63 deletions(-) delete mode 100644 src/rk65c02cli.c diff --git a/src/rk65c02cli.c b/src/rk65c02cli.c deleted file mode 100644 index 1e24534..0000000 --- a/src/rk65c02cli.c +++ /dev/null @@ -1,63 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "bus.h" -#include "instruction.h" -#include "rk65c02.h" - - -/* -int -main(void) -{ - bus_t b; - - b = bus_init(); - - bus_write_1(&b, 0, OP_INX); - bus_write_1(&b, 1, OP_NOP); - bus_write_1(&b, 2, OP_LDY_IMM); - bus_write_1(&b, 3, 0x1); - bus_write_1(&b, 4, OP_TSB_ZP); - bus_write_1(&b, 5, 0x3); - bus_write_1(&b, 6, OP_JSR); - bus_write_1(&b, 7, 0x09); - bus_write_1(&b, 8, 0x0); - bus_write_1(&b, 9, OP_STP); - - rk6502_start(&b, 0); - - bus_finish(&b); -} -*/ - - -int main() -{ - char* input, shell_prompt[100]; - - rl_bind_key('\t', rl_complete); - - while(true) { - snprintf(shell_prompt, sizeof(shell_prompt), "> "); - - input = readline(shell_prompt); - - if (!input) - break; - - add_history(input); - - free(input); - } - return EXIT_SUCCESS; -} - From faec05b87ede65c13ddc5abf420c5fae76163f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Tue, 10 Apr 2018 12:17:59 +0200 Subject: [PATCH 40/87] Start doxifying the documentation. --- src/rk65c02.c | 45 --------------------------------------------- src/rk65c02.h | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 45 deletions(-) diff --git a/src/rk65c02.c b/src/rk65c02.c index a2055f7..4294ff7 100644 --- a/src/rk65c02.c +++ b/src/rk65c02.c @@ -18,12 +18,6 @@ void rk65c02_exec(rk65c02emu_t *); -/** - * \brief Prep the emulator, load code from file, pass bus config optionally. - * \param path Path to ROM file to be loaded. - * \param load_addr Address on the bus where ROM should be loaded. - * \param b Pre-existing bus configuration, pass NULL if default requested. - */ rk65c02emu_t rk65c02_load_rom(const char *path, uint16_t load_addr, bus_t *b) { @@ -42,9 +36,6 @@ rk65c02_load_rom(const char *path, uint16_t load_addr, bus_t *b) return e; } -/* - * Prepare the emulator for use, set initial CPU state. - */ rk65c02emu_t rk65c02_init(bus_t *b) { @@ -69,9 +60,6 @@ rk65c02_init(bus_t *b) return e; } -/* - * Assert the IRQ line. - */ void rk65c02_assert_irq(rk65c02emu_t *e) { @@ -87,9 +75,6 @@ rk65c02_assert_irq(rk65c02emu_t *e) rk65c02_start(e); } -/* - * Respond to interrupt and start the interrupt service routine. - */ void rk65c02_irq(rk65c02emu_t *e) { @@ -163,9 +148,6 @@ rk65c02_exec(rk65c02emu_t *e) } -/* - * Start the emulator. - */ void rk65c02_start(rk65c02emu_t *e) { @@ -177,9 +159,6 @@ rk65c02_start(rk65c02emu_t *e) { } } -/* - * Execute as many instructions as specified in steps argument. - */ void rk65c02_step(rk65c02emu_t *e, uint16_t steps) { @@ -273,27 +252,3 @@ rk65c02_panic(rk65c02emu_t *e, const char* fmt, ...) /* TODO: run some UI callback. */ } -/* -int -main(void) -{ - bus_t b; - - b = bus_init(); - - bus_write_1(&b, 0, OP_INX); - bus_write_1(&b, 1, OP_NOP); - bus_write_1(&b, 2, OP_LDY_IMM); - bus_write_1(&b, 3, 0x1); - bus_write_1(&b, 4, OP_TSB_ZP); - bus_write_1(&b, 5, 0x3); - bus_write_1(&b, 6, OP_JSR); - bus_write_1(&b, 7, 0x09); - bus_write_1(&b, 8, 0x0); - bus_write_1(&b, 9, OP_STP); - - rk65c02_start(&b, 0); - - bus_finish(&b); -} -*/ diff --git a/src/rk65c02.h b/src/rk65c02.h index 4d624df..0370181 100644 --- a/src/rk65c02.h +++ b/src/rk65c02.h @@ -1,3 +1,6 @@ +/** @file rk65c02.h + * @brief Public functions for managing rk65c02 emulator. + */ #ifndef _RK6502_H_ #define _RK6502_H_ @@ -76,14 +79,43 @@ struct rk65c02emu { typedef struct rk65c02emu rk65c02emu_t; +/** + * @brief Initialize the new emulator instance. Set initial CPU state. + */ rk65c02emu_t rk65c02_init(bus_t *); + +/** + * @brief Start the emulator. + */ void rk65c02_start(rk65c02emu_t *); + +/** + * @brief Execute as many instructions as specified in steps argument. + */ void rk65c02_step(rk65c02emu_t *, uint16_t); char *rk65c02_regs_string_get(reg_state_t); void rk65c02_dump_regs(reg_state_t); void rk65c02_dump_stack(rk65c02emu_t *, uint8_t); + +/* + * @brief Assert the IRQ line. + */ +void rk65c02_assert_irq(rk65c02emu_t *); + +/** + * @brief Respond to interrupt and start the interrupt service routine. + */ void rk65c02_irq(rk65c02emu_t *); + void rk65c02_panic(rk65c02emu_t *, const char*, ...); + +/** + * @brief Prep the emulator, load code from file, pass bus config optionally. + * @param path Path to ROM file to be loaded. + * @param load_addr Address on the bus where ROM should be loaded. + * @param b Pre-existing bus configuration, pass NULL if default requested. + * @return New instance of the emulator prepared to run the ROM. + */ rk65c02emu_t rk65c02_load_rom(const char *, uint16_t, bus_t *); #endif From 901e9626d577e586d524655c2ba391f33c0534c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kujawa?= Date: Tue, 10 Apr 2018 12:18:19 +0200 Subject: [PATCH 41/87] Add initial Doxyfile. --- src/Doxyfile | 2482 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2482 insertions(+) create mode 100644 src/Doxyfile diff --git a/src/Doxyfile b/src/Doxyfile new file mode 100644 index 0000000..e0aaf65 --- /dev/null +++ b/src/Doxyfile @@ -0,0 +1,2482 @@ +# Doxyfile 1.8.14 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "rk65c02" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = ../docs + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via Javascript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have Javascript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: https://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://doc.qt.io/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://doc.qt.io/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://doc.qt.io/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/ + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /