diff --git a/.github/workflows/headless.yml b/.github/workflows/headless.yml new file mode 100644 index 00000000..2934e3ad --- /dev/null +++ b/.github/workflows/headless.yml @@ -0,0 +1,43 @@ +name: Headless + +# The live test's disc on the core with no display and no GPU: headless.c +# for a frontend and an OpenGL that does nothing (headless_gl.py). The +# program's verdicts on itself, its sound sample for sample, both SH4 +# timings and the MMU, states saved and loaded and every frame run ahead +# (each the same, byte for byte, as the frames run straight), code that +# rewrites itself, a NAOMI cartridge and romsets: tools/threads/headless.sh. + +on: + push: + paths: + - 'core/**' + - 'tools/threads/headless*' + - 'tools/threads/live_*' + - 'Makefile*' + - '.github/workflows/headless.yml' + pull_request: + paths: + - 'core/**' + - 'tools/threads/headless*' + - 'tools/threads/live_*' + - 'Makefile*' + - '.github/workflows/headless.yml' + workflow_dispatch: + +permissions: + contents: read + +jobs: + headless: + runs-on: ubuntu-24.04 + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - name: Install the compiler and OpenGL headers + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends build-essential libgl1-mesa-dev python3 + - name: Build the core + run: make -j"$(nproc)" + - name: The disc with no display, its states and run-ahead among it + run: sh tools/threads/headless.sh parallel_dc_libretro.so diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index 69199af5..2a3353b4 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -6,7 +6,9 @@ name: Sanitizers # the HDR output, the recompiler on: any report fails the run # (tools/vulkan/vk_sanitize.sh). And with AddressSanitizer, the core # unloaded after its frames with OpenGL and the process ended, with nothing -# left over for LeakSanitizer (tools/threads/unload_leaks.sh). +# left over for LeakSanitizer (tools/threads/unload_leaks.sh). And with +# each, states saved and loaded with every frame run ahead, on OpenGL +# (tools/gl/gl_ahead.sh). on: push: @@ -14,6 +16,8 @@ on: - 'core/**' - 'tools/threads/unload_leaks.sh' - 'tools/gl/gl_frames.c' + - 'tools/gl/gl_ahead.sh' + - 'tools/gl/ahead_check.py' - 'tools/vulkan/vk_sanitize.sh' - 'tools/vulkan/vk_hdr.c' - 'tools/hdr/hdr_post_ref.*' @@ -25,6 +29,8 @@ on: - 'core/**' - 'tools/threads/unload_leaks.sh' - 'tools/gl/gl_frames.c' + - 'tools/gl/gl_ahead.sh' + - 'tools/gl/ahead_check.py' - 'tools/vulkan/vk_sanitize.sh' - 'tools/vulkan/vk_hdr.c' - 'tools/hdr/hdr_post_ref.*' @@ -61,6 +67,8 @@ jobs: fi - name: Frames on every renderer, 24-bit and HDR run: VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json SANITIZE=${{ matrix.sanitizer }} sh tools/vulkan/vk_sanitize.sh + - name: States saved and loaded, every frame run ahead + run: SANITIZE=${{ matrix.sanitizer }} sh tools/gl/gl_ahead.sh - name: The core unloaded, nothing left over (LeakSanitizer) if: matrix.sanitizer == 'address' run: sh tools/threads/unload_leaks.sh diff --git a/.github/workflows/vulkan-negotiation.yml b/.github/workflows/vulkan-negotiation.yml index a2969fde..8bdeca84 100644 --- a/.github/workflows/vulkan-negotiation.yml +++ b/.github/workflows/vulkan-negotiation.yml @@ -23,6 +23,8 @@ name: Vulkan negotiation # sorter's sort against a stable sort by < (tools/rend/sort_pairs_test.sh). # And polygons drawn outside a clipping rectangle, with OpenGL and OpenGL # ES, each renderer (tools/gl/gl_clip.sh). +# And a state saved while a render to a texture is still on the card, every +# frame run ahead, with OpenGL and OpenGL ES (tools/gl/gl_ahead.sh). on: push: @@ -115,6 +117,8 @@ jobs: sh tools/gl/gl_dma.sh - name: Polygons drawn outside a clipping rectangle, with OpenGL and OpenGL ES (each renderer) run: sh tools/gl/gl_clip.sh + - name: A state saved while a render to a texture is on the card, every frame run ahead (OpenGL and OpenGL ES) + run: sh tools/gl/gl_ahead.sh - name: The translucent sorter's sort (radix, stable, as a sort by <) run: sh tools/rend/sort_pairs_test.sh - name: The temporal upscaler's side in the core (strips matched, the request, the inputs) diff --git a/core/serialize.cpp b/core/serialize.cpp index ae8e179a..bdaf16ba 100644 --- a/core/serialize.cpp +++ b/core/serialize.cpp @@ -416,9 +416,12 @@ void ra_unserialize_fail(void) unserialize_failed = true; } +/* (Nothing to copy is nothing to do: a part of the machine this one does + * not have - the Dreamcast's SRAM - is no memory and a size of 0, and no + * memory is no pointer to copy from or to, even for none of it.) */ bool ra_serialize(const void *src, unsigned int src_size, void **dest, unsigned int *total_size) { - if ( *dest != NULL ) + if ( *dest != NULL && src_size != 0 ) { memcpy(*dest, src, src_size) ; *dest = ((unsigned char*)*dest) + src_size ; @@ -430,7 +433,7 @@ bool ra_serialize(const void *src, unsigned int src_size, void **dest, unsigned bool ra_unserialize(void *src, unsigned int src_size, void **dest, unsigned int *total_size) { - if ( *dest != NULL ) + if ( *dest != NULL && src_size != 0 ) { /* Reject any read that would run past the end of the frontend-supplied * input buffer. base==NULL means the limit is unknown (legacy callers); diff --git a/tools/gl/ahead_check.py b/tools/gl/ahead_check.py new file mode 100644 index 00000000..8dddb362 --- /dev/null +++ b/tools/gl/ahead_check.py @@ -0,0 +1,60 @@ +"""Judges two pictures of the test disc built with -DLIVE_KEEP +(tools/threads/live_disc.py --keep), in vk_hdr's form (a 16-byte header: +"VKHR", the Vulkan format, the width, the height; then RGBA8 rows, top +first; 640x480, the disc's 320x240 doubled): one from a plain run, one from +a run of one frame less with every frame run ahead (gl_frames GLF_AHEAD). +The disc renders a picture to a texture once, at the very end of a frame, +and then only draws with it, in the screen's top left: yellow, its top left +quarter cyan, at x and y 10-137. A state saved right after that frame is +saved while the picture is still on its way; the run ahead loads such a +state every frame, so it shows the picture only if the state has it. The +two pictures have to be the same, and show it. + + python3 ahead_check.py plain.raw ahead.raw""" +import struct +import sys + +def load(path): + try: + data = open(path, 'rb').read() + except OSError as e: + print(' FAIL: no picture: %s' % e) + return None + if len(data) < 16: + print(' FAIL: %s is not a picture' % path) + return None + magic, fmt, w, h = struct.unpack('<4I', data[:16]) + if magic != 0x52484b56 or fmt != 37 or (w, h) != (640, 480) or len(data) < 16 + w * h * 4: + print(' FAIL: %s is not a 640x480 RGBA8 picture: %x %d %dx%d' % (path, magic, fmt, w, h)) + return None + return data + +def px(data, x, y): + o = 16 + (y * 640 + x) * 4 + return tuple(data[o:o + 3]) + +def main(): + if len(sys.argv) != 3: + sys.stderr.write(__doc__ + '\n') + return 2 + plain, ahead = load(sys.argv[1]), load(sys.argv[2]) + if plain is None or ahead is None: + return 1 + fails = 0 + for name, data in (('plain', plain), ('ahead', ahead)): + for (x, y), want, what in (((40, 40), (0, 255, 255), 'cyan'), ((110, 40), (255, 255, 0), 'yellow'), + ((110, 110), (255, 255, 0), 'yellow'), ((40, 110), (255, 255, 0), 'yellow')): + got = px(data, x, y) + if got != want: + print(' FAIL: %s: the kept picture at %d,%d is %s, not %s' % (name, x, y, got, what)) + fails += 1 + if plain[16:] != ahead[16:]: + n = sum(1 for i in range(16, len(plain), 4) if plain[i:i + 3] != ahead[i:i + 3]) + print(' FAIL: the run ahead is not the plain run: %d pixels differ' % n) + fails += 1 + if fails == 0: + print(' ok: the picture rendered to a texture is in the state, run ahead as run plain') + return 1 if fails else 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tools/gl/gl_ahead.sh b/tools/gl/gl_ahead.sh new file mode 100644 index 00000000..960e6d04 --- /dev/null +++ b/tools/gl/gl_ahead.sh @@ -0,0 +1,74 @@ +#!/bin/sh +# A state saved while a render to a texture is still on the graphics card, +# with OpenGL and OpenGL ES 3 on Mesa's surfaceless EGL (gl_frames.c), as +# tools/gl/gl_dma.sh has it. The test disc built with -DLIVE_KEEP +# (tools/threads/live_disc.py --keep) renders a picture to a texture once, +# at the very end of a frame, and from the next frame on only draws with +# it. Every frame run ahead (gl_frames GLF_AHEAD: a state saved, a frame +# run, the state loaded) has to show what a plain run shows, the picture +# among it: a state carries video memory as the console has it, renders +# that have not been fetched off the card included (tools/gl/ahead_check.py). +# +# sh tools/gl/gl_ahead.sh [core.so] +# +# KEEP=1: the pictures and logs are kept, in $TMPDIR/gl_ahead.. +# SANITIZE=address|undefined: the core is one built with DEBUG_ASAN=1 or +# DEBUG_UBSAN=1, and runs with its sanitizer's runtime; what it reports +# fails the run. +set -e +cd "$(dirname "$0")/../.." +CORE=$(readlink -f "${1:-parallel_dc_libretro.so}") +WORK=${TMPDIR:-/tmp}/gl_ahead.$$ +mkdir -p "$WORK" +[ -n "${KEEP:-}" ] || trap 'rm -rf "$WORK"' EXIT +${CC:-cc} -O1 -g -Wall -o "$WORK/gl_frames" tools/gl/gl_frames.c -lEGL -lGL -ldl +python3 tools/threads/live_disc.py "$WORK/test.gdi" --keep +fails=0 +preload= +case "${SANITIZE:-}" in +'') ;; +address) + preload=LD_PRELOAD=$(${CC:-cc} -print-file-name=libasan.so) + # (the recompiler's memory faults are its own: ASan's handler is kept out of them) + export ASAN_OPTIONS="halt_on_error=1:detect_leaks=0:handle_segv=0:log_path=$WORK/san" ;; +undefined) + preload=LD_PRELOAD=$(${CC:-cc} -print-file-name=libubsan.so) + export UBSAN_OPTIONS="halt_on_error=1:print_stacktrace=1:log_path=$WORK/san" ;; +*) + echo "gl_ahead.sh: SANITIZE is address or undefined" >&2; exit 2 ;; +esac + +# The picture is rendered in frame 82 and drawn from 83 on; the run ahead +# starts well before. +for api in gl gles; do + echo "$api" + # (GLF_GLES set at all asks for OpenGL ES) + gles=; [ $api = gles ] && gles=GLF_GLES=1 + ok=1 + for run in plain ahead; do + ahead=; frames=101 + [ $run = ahead ] && { ahead=GLF_AHEAD=5; frames=100; } + rm -rf "$WORK/dir"; mkdir -p "$WORK/dir" + if ! env $preload $gles $ahead GLF_DIR="$WORK/dir" "$WORK/gl_frames" "$CORE" "$WORK/test.gdi" "$WORK/$api-$run.raw" $frames \ + > "$WORK/$api-$run.log" 2>&1; then + echo " FAIL: the $api $run run failed:"; tail -5 "$WORK/$api-$run.log" + ok=0 + fi + if [ -n "$preload" ] && ls "$WORK"/san.* > /dev/null 2>&1; then + echo " FAIL: the $api $run run: the $SANITIZE sanitizer"; head -20 "$WORK"/san.* + rm -f "$WORK"/san.* + ok=0 + fi + done + if [ $ok = 1 ]; then + python3 tools/gl/ahead_check.py "$WORK/$api-plain.raw" "$WORK/$api-ahead.raw" || fails=$((fails + 1)) + else + fails=$((fails + 1)) + fi +done + +if [ $fails != 0 ]; then + echo "gl_ahead: $fails failed" + exit 1 +fi +echo "gl_ahead: all passed" diff --git a/tools/gl/gl_frames.c b/tools/gl/gl_frames.c index cb162085..13d4f6b0 100644 --- a/tools/gl/gl_frames.c +++ b/tools/gl/gl_frames.c @@ -13,7 +13,11 @@ * an OpenGL core one; GLF_EVERY=1 prints a checksum of every frame the * core hands over, "frame N SIZE SUM", for runs to be compared frame for * frame (the frames a game writes into its framebuffer itself among them, - * which a run's last frame seldom is). + * which a run's last frame seldom is). GLF_AHEAD=n runs the frames from + * the n-th on as a frontend's run-ahead of one frame does: the frame, a + * state saved, the frame after it (the one shown) and the state loaded + * back. The picture is then the one a plain run of one frame more shows, + * for whatever a state carries (tools/gl/gl_ahead.sh). * * Built with: cc -o gl_frames gl_frames.c -lEGL -lGL -ldl */ #define _GNU_SOURCE @@ -179,6 +183,12 @@ int main(int argc, char **argv) void (*retro_run)(void); void (*retro_unload_game)(void); void (*retro_deinit)(void); + size_t (*retro_serialize_size)(void); + bool (*retro_serialize)(void *, size_t); + bool (*retro_unserialize)(const void *, size_t); + void *state = NULL; + size_t state_size = 0; + int ahead_at = getenv("GLF_AHEAD") ? atoi(getenv("GLF_AHEAD")) : -1; struct retro_game_info game; struct retro_system_av_info av; EGLDisplay display; @@ -262,6 +272,9 @@ int main(int argc, char **argv) CORE(retro_load_game); CORE(retro_get_system_av_info); CORE(retro_run); + CORE(retro_serialize_size); + CORE(retro_serialize); + CORE(retro_unserialize); CORE(retro_unload_game); CORE(retro_deinit); @@ -312,6 +325,26 @@ int main(int argc, char **argv) { frame_no = i; retro_run(); + if (ahead_at >= 0 && i >= ahead_at) + { + /* the state after this frame; the frame after it, shown; back to the state */ + if (!state) + { + state_size = retro_serialize_size(); + state = malloc(state_size); + } + if (!state || !retro_serialize(state, state_size)) + { + fprintf(stderr, "gl_frames: the state could not be saved (frame %d)\n", i); + return 1; + } + retro_run(); + if (!retro_unserialize(state, state_size)) + { + fprintf(stderr, "gl_frames: the state could not be loaded (frame %d)\n", i); + return 1; + } + } } if (last_w == 0 || last_h == 0) @@ -352,5 +385,6 @@ int main(int argc, char **argv) retro_unload_game(); retro_deinit(); free(pixels); + free(state); return 0; } diff --git a/tools/threads/headless_gl.py b/tools/threads/headless_gl.py index 795e7e78..67c5423c 100644 --- a/tools/threads/headless_gl.py +++ b/tools/threads/headless_gl.py @@ -71,8 +71,24 @@ unsigned GL_CC glCheckFramebufferStatus(unsigned target) { return 0x8CD5; } unsigned GL_CC glClientWaitSync(void *sync, unsigned flags, unsigned long long timeout) { return 0x911A; } static void *scratch; static void *map(void) { if (!scratch) scratch = calloc(1, 64 << 20); return scratch; } -void *GL_CC glMapBufferRange(unsigned target, long offset, long length, unsigned access) { return map(); } -void *GL_CC glMapBuffer(unsigned target, unsigned access) { return map(); } +/* What is read back from the card is the same bytes every time, whatever + * the core wrote through a mapping before: a picture rendered to a texture + * and fetched is then one thing, run after run and from a state loaded (and + * not zeros: one that never got to video memory is told from one that did). + * Reading through the mapping the core writes through gave whatever it last + * wrote there, which is no part of the machine. */ +#define READ_BYTE 0x5A +static void *read_back; +static void *read_map(void) { if (!read_back) { read_back = malloc(64 << 20); if (read_back) memset(read_back, READ_BYTE, 64 << 20); } return read_back; } +void *GL_CC glMapBufferRange(unsigned target, long offset, long length, unsigned access) { return (access & 0x0001 /* GL_MAP_READ_BIT */) && !(access & 0x0002) ? read_map() : map(); } +void *GL_CC glMapBuffer(unsigned target, unsigned access) { return access == 0x88B8 /* GL_READ_ONLY */ ? read_map() : map(); } +/* (into the core's own memory: a pixel pack buffer's offset is no pointer) */ +void GL_CC glReadPixels(int x, int y, int w, int h, unsigned format, unsigned type, void *pixels) +{ + if (format == 0x1908 /* GL_RGBA */ && type == 0x1401 /* GL_UNSIGNED_BYTE */ && (size_t)pixels >= 0x10000 && w > 0 && h > 0) + memset(pixels, READ_BYTE, (size_t)w * (size_t)h * 4); +} +void GL_CC glGetBufferSubData(unsigned target, long offset, long size, void *data) { if (data && size > 0) memset(data, 0, (size_t)size); } ''' def stack_bytes(prototypes): @@ -100,6 +116,7 @@ def main(): for line in SPECIAL.splitlines() if line and not line.startswith((' ', '{', '}', 'static', '/', '#'))} print('/* written by tools/threads/headless_gl.py */') print('#include ') + print('#include ') print(SPECIAL) for name in names: if not name.startswith('gl') or name in special: diff --git a/tools/threads/live_disc.py b/tools/threads/live_disc.py index e0a9d495..17df32b7 100755 --- a/tools/threads/live_disc.py +++ b/tools/threads/live_disc.py @@ -2,7 +2,7 @@ """Write a bootable GD-ROM image (GDI + three track files) around the bare-metal test program in live_prog.c, for tools/threads/live.sh. -Usage: live_disc.py out.gdi [--no-region-array | --mmu-align | --slant | --hscale | --cull | --atlas | --dma | --clip] +Usage: live_disc.py out.gdi [--no-region-array | --mmu-align | --slant | --hscale | --cull | --atlas | --dma | --clip | --keep] live_disc.py out.elf The program is carried here already compiled, so the test needs no SH-4 @@ -3343,6 +3343,421 @@ e0ffff03000000e0111111113c0000ffe0ffc000400000006400000000000000 000000000000803fffffffff """) + +# The program built with -DLIVE_KEEP (live_prog.c): with a picture +# rendered to a texture once, at the very end of a frame, and only ever +# drawn with after that, for a state saved while it is still on its way +# (tools/gl/gl_ahead.sh). +PROGRAM_KEEP = bytes.fromhex(""" +01df02d02b40090000f0008c240e018c020005d119206b200e40543402004227 +01170b0009000900fefcffff020005d119205b200e4044340200422601160b00 +09000900fefcffff1b4429000b0009000344422642605a200224074402000116 +42160b00090002000b0009000e440b0009006a446a000b00090066446a004225 +0b0009006a000b000900020101e00b000e4100e00b000900034f01e00b00074f +00e00b0009000900000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +000000000000000004d105d002600221420002704e402b00090009004440018c +240000ff00000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +000000000000000016d0026116d229211ad208e522633031128908721045f98b +13d33031068913d3392113d33b210be402a009000dd10ae411d33b2103a00900 +215109e45834111004510ed33921184408444b2114100cd22263017332223800 +2b000900000000ff00f0ffff00000010000000e000f0ff000000000c0040018c +76010000ff03ffff4040018c220203d12e41fdff2e420b0009000900a800018c +8df09df19df29df39df49df59df69df7830410f020f030f040f050f060f070f0 +3df05a000b0009005a440df24df21df25a000b00090048f44cf55af50b000900 +48f00b0009000af40b0009000dd10ed2222104710dd2222104710dd222210471 +0cd2222101e10cd212220cd200e3322204721222f0720ad33222147212220b00 +0900090000e000ac0100008000e1000c0920000100000001e86c5fa0106c5fa0 +00e0000c43631843436128411b234b23136418444b2304d210e132221041fc8f +04720b0009000900000000a409d112610f92292108d307d42365336242661367 +636159217231028b10431042f68b0b000900ff030c815fa080841e0002d11260 +01910b001920ff030c815fa0862f962f224f436804d90b4909008230fb8b264f +f6690b00f66809003c06018cf47ff361319221214d6229444c602d9323673927 +7b2011812c60128100e013815d6229455c6029233b2014812c6015810fd101e2 +20211871a0e220210dd20ed1206008c8018f1041fa8b0ad1106008c80989f362 +09d706e1256331271041fb8f01e00b000c7ffcaf00e0302000ff090084705fa0 +18705fa0a086010080705fa027d100e220210c71602163621942047120210871 +a0e2202122d223d1206008c8018f1041fa8b1fd1106008c8348d06e11ed34562 +21231041fb8b1ad21ad1206008c8018f1041fa8b16d1106008c8258d68261d8d +536400e014d70174180063610a3101416362017202e33232078d017105a001e1 +194224041041098d027071622d62036301736233f48f2405f5af104107d11061 +0b0001e00b0000e00b0000e084705fa018705fa0a086010080705fa09c705fa0 +862f962fa62fb62f224f907f43680ae6f36572d472d00b400c750820018bbba0 +09e0fc840fc903886c8f01e0f3600c7002840188668f01e0f3600c7003840288 +608f01e0f3620c7225840c63284326840c6118613b2127840c600b21b9931c33 +b8922633018b9ea001e08828488d83605cd222621032018b97a002e00ee6f365 +59d457d00b400c750820018b8fa009e0f3600c700184118801898aa003e0f360 +0c7004840188018985a003e0f3600c7005840288018980a003e0f3620c722b84 +0c6328432c840c6118613b212d840c622b217e922c317d922631018b6fa003e0 +64e6f36541d43ed00b400c750820688df365f36a107af36800eb6c9414a00ce9 +38d2122248a0090010420a8d0177346040c8f98d7b6043610c416f841b20f3af +6f801049088d087ab0280178a36300e78366f076ebaf08e200e12dd64c940ca0 +0ae01c311042068dff736821f9891c314a21f7af1d611040068954622c621842 +2a217363f1af7362f06001882b8bf36001840288298b1fd22a21f362fa840c62 +136319433032228ff3622b840c621c611032ffe00a600840707f264ff66bf66a +f6690b00f668f7af0900f5af0900f3af0900f1af0900efaf0900edaf0900ebaf +0900e9af09e0e7af04e0e5af04e0e3af04e0a8fd2b018000211009001832018c +ec06018c1480018c0c32018c0032018c00800000ffff00000ad1422104715221 +01e109d21222087212220472122207d1226001c8018d1041fa8b05d1126001c8 +0b00290004745fa00c745fa000093d0018745fa0626113671947014725922821 +ffe22a62436300432903136001402444136019407a2001c8108943605c302a23 +063429073a2773611841236318433c333b21203729022b210b00122643605830 +2a2346302907eeaf3a27000249d1126249d11032048d00e248d122210b0000e0 +47d148d2122248d221521032718b47d112600188028d11e00b00090044d345d1 +3111047144d2292144d645d772166c921b222316726230325d8f00e341d23222 +736301e77223226360922033558b39d2215337d22033df8f13e03bd23bd33112 +3bd334d733173bd773123bd226164e921b22271639d222623032cd8f16e038d2 +2dd3392243932b23371624d305e7722332d332672fd33037318b32d32fd73227 +29d7735730372c8b22d3335728d33037b28f18e02cd778162a977b2119162bd1 +01e332212ad1126321d11033a48f19e025d338167b22291624d102e2222124d1 +126220d110320e8f00e220d122210b0001e00b0011e00b0012e00b0013e00b00 +17e00b0018e00b001ae01601c0003601340109000000001044332211100000ff +88776655040000100050018c4040018c5555aaaafc5f018c00f0ff1f0040018c +001000104440018cfc6f018c0df0ad0bb0b0b0b0c0c0c0c00030001008300010 +0070018cc1c1c1c100400010000000ff08400010862f962ff45110d2222210d8 +422810d0522010d4122410d5622510d6722610d7122710d3f25992238302f359 +9222122800e11220122412251226122712238302f6690b00f6680900000000e0 +040000e0080000e00c0000e0100000e0140000e0180000e01c0000e0862f962f +a62fb62fc62fd62fe62f224ff07f536b636a7369fd5e2ad11b24431f29dd422d +00e829d1822129d282220871822108728222087182210872822208718221830d +e21f611f722f22dcfc575366a3650b4cb364e21ffc52211f922f2367b366a365 +0b4c93641bdee21fa11f922ffc57b366a3650b4cb364f351122d17d112d21222 +12d182210872822208718221087282220871822108728222830de21ffc51111f +922f1367b366a3650b4c9364107f264ff66ef66df66cf66bf66af6690b00f668 +00000081000000e0040000e0080000e0b40b018c0000803e00000024224ff87f +f351111f722f6367536643650cd10b4100e40cd200e112220bd3122304731223 +04731223047312230473122304731223047312238302087f264f0b0009000900 +1c0c018c000000e0040000e0862f962fa62fb62fc62fd62fe62ff75cf85bf95e +24d14b2124d2122224da25d1122a25d9522925d8622825d000e1122024d51225 +24d6122624d312238302436d2b2dd222b22ae229c22812201225722612238302 +d222b22afb5bb229c22812201bdbb225722612238302d222fa5dd22ae229c228 +b220122572261223830215dddb244222fa54422afb5dd229c228b220b2257226 +12238302f66ef66df66cf66bf66af6690b00f66800000080000000e0040000e0 +000000c0080000e00c0000e0100000e0140000e0180000e01c0000e00000803f +000000f0862f962fa62fb62fc62fd62fe62f224fbc7fc07f9bd112619bd22665 +266622679ad2266322625b216b217b213b212b2144e0160f96d11060fec90021 +ffe295d322230473222300e204732123106001cb002191d1126091d3392005cb +022118718fd33221f471222110718ed222216c718dd222219c718dd222210471 +8cd2222110718cd222218cd18cd212228cd212228cd18dd222218dd600e38dd8 +8dd412e745330a02094223611c3113652c355c353369583953609c3008408e01 +122601731047ed8f047684d184d2222184d185d22221047184d22221047184d2 +222184d1c59222210471c392222104711fe222210471be9222217fd27fd310e1 +32221041fc8f04727dd1b59222213c717cd222210871077222217bd27bd3ac91 +32221041fc8f047279d17ad77ad310e27221a3961c3632261042f98f047177d1 +77d2222104719a92222176d276d310e132221041fc8f04721fe174d2122274d2 +122274d100e002ea00e973d807e41fe58697869317e6d62f019d230df66d8c21 +1043058d017726657035f989f8af63616ad227536ad22033648d48e001e1160f +10e168d212220472122267d267d30ce132221041fc8f047200e163d212220472 +122263d163d2222163d164d2222164d12e4164d1ffe700e308e2722131111042 +fb8f08715fd360d1122360d260d1292148977b2111135fd1122200e111125ed2 +12225ed212225ed105e222215dd00b40090040e7fc3700175bd1eee233932021 +5ad71c3720275ad71c37202759d71c3720271043f38f017157d458d00b4005e5 +082001ee028de61fb4a0090054d00b40090054d454d10b41092454dcc36dc51f +bda200ee52d2205335d22033978996af01e1007ce003ff7f8001800000101f7c +e00700f836010048846c5fa0f46c5fa0c0785fa00400d8ff0800d8ff44805fa0 +000080003f7d1700000020000401150000007f020000df0177df270000001000 +20805fa028815fa08c805fa0000000030c0010a59032018cabaaaaaa88805fa0 +17b7d138000010a500000002000080200000003004905fa0001000a403030303 +74805fa000ff000000825fa080800000002006a4050505050606060614905fa0 +1ffc0000000006a4070707071c985fa01c9c5fa0004006a4ffff0000c040018c +11111111380000ff000018a500000080180018a54c0e00a02c805fa000001800 +a800018c0040018c000000100050018c00f0ff1f443322114040018c000000ff +100000ff2c0a018c000023ac0000ffff0000feff0000fdff5eb000006c06018c +6600018cff7fffff6c00018c5832018c0041018cbd9562d462d00b4009000820 +028fe61f42af09005fd460d00b4005e50820018ba3a002ee5dd8ab9b59da5dd9 +b3650b4a83640820018b9aa002eea1900c389038f58fb36553d454d00b4008e5 +0820018b8fa003ee959553d44dd00b4009000820028f10e187a003ee4fd650d2 +20677c6764633c633037028d10417ea004eef58f01724bd17b9210677c674ad3 +1c3330633c633037028d1042a8a005eef38f017145d16c9210677c6744d31c33 +30633c633037028d10429ba006eef38f017135d435d00b4002e50820018b93a0 +08ee3cd13cd2222104715592222101e13ad21222087212220472122238d12260 +01c8018d1041fa8b36d1126001c8028ffc717ba009ee00e222210471126001c8 +018975a00aee30d1379202a009000c8d017110677c672dd31c3330633c633037 +f58d10420beeb1aee61f29d110611c61249220315e8f0cee26d110611c612031 +5a8f07ee24d110611c612031568b23d110611c612031538b00ee97aee61f95ae +e61f93aee61f91aee61f8faee61f8daee61f00280004004000180010ee000900 +0000200c9809018c5eb000006c06018c0000210c0028210c0000220c8032018c +000020ac000021ac0000ffff000022ac0000feff04745fa00000230c0c745fa0 +801a060018745fa0000023ac0000fdff001023ac002820ac002821ac004022ac +54aee61f52aee61f50aee61f4eaee61f4caee61f4aaee61f48aee61f46aee61f +44aee61f017993600888038f936800a1017e936807e00d4846911928936001c9 +0b2870e0860fd26af553326bf3667076b3651ed70b47a364071ff36778778366 +b3651bd00b40a36440e2fc322e51f7501030318f70e02f5127922921fe022031 +028de82ed1a001eecc8ff366860f7076a3650ed70b47a364036bf36678768365 +0cd00b40a36440e2fc322e51103b0189bda002ee2f51089339212c522031b28d +017902ee0aa0e51f00030103d409018c1000018c2c00018c01eee51f59d10b41 +00e859d00b400900036158d956db0b4b136aa230058f0361a030ffe22a381049 +f58b63e1163801ee028fef1f07a209004fd850d1122850d10b41090082624fd3 +4ad1326001c8018d1041fa8b4bd11261136001c9081f136001c8018910a202e0 +43d1126343d11033018b0ba203e040d1126040d11032038dff88018904a201e0 +3bd11260ff885e893ad222213cd13dd2222104713cd222210471629222213bd1 +3bd22221147101e2222113622fd1226001c8018d1041fa8b2dd112611c611360 +0688018909a204e029d22260294019403088028d747202a204e022632dd22033 +0189fea105e022d222d3322224d22ad33222047224d33222047228d332220472 +27d33222047227d3322227d727d320e2322701731042fb8f04771cd21cd33222 +147201e33222236310d2326001c8018d1042fa8b0ed22260ff88048922600c60 +07880089181fa0a1090057afe51f55afe51ff5500470051fe3600a882889f8ae +00e902200c06018c3c06018c80841e0000e100ac785634128c05018c186c5fa0 +00e000ac0000008000e1000c046c5fa000e0000c56657273220000800c010022 +000000020000006414e000ac0000dec0047d73d1103d018912adc51f71d8836c +057cf36a547a70db84600c69ab80f3640b4b5f74036168e0982929022031028d +160f19a103eeab840c61c8909b201030028dc03812a103eee68b64d8836e147e +63dd64dc64db65da0b4d0900051f8669f36678769365f3640b4c747440e3fc33 +3e52f361707110320189f9a004eef3617471a490fe0210320189f3a004ee3c52 +f5512a219a21b9213f533a22a9222b211821028de038e7a004eed58b50d00b40 +f36b036a4fd9936e187e4fdd6c7b4fdc96680b4d83640038028d02e0d6a005ee +0a2868e0860fb365f3640b4c68740038028d40e2cca006eefc322b51b031028d +903ec7a006eee38b41d1181241d1191241d93dd80b48936440dbf3640b4b6074 +3fd40b4807eef3640b4b64740b489364f3643cd10b41687440e7fc377a527851 +1032478fe51f36d11a17f3656c75f3642ed00b406874f36432d10b41687440e1 +fc311a521951103208ee338fe51f2ed00b4009ee01882d8fe51f2cd00b400aee +0188278fe51f2ad483600b400bee20d428d00b40090028d110301b8fe51f27d4 +24d00b4009000188148fe51f24d140e0fc30181000e11a10f3656875f36421d1 +0b41607440e3fc333a52385110320cee028de51f42ae0900a36435a083608000 +8032018c5032018c4800018c3c32018c6600018c5000018cf2830070f3830070 +8400018c2432018c7200018c7a00018c0000803f00000040010004008005018c +010024008605018c8a00018c9800018c01000c006805018c000080bf01000080 +785634127605018c0b40090044d800e1122844d10b41090082627e9110320dee +088fe51ff36440d00b4068740788138f00eee51ff2ad0900f0ade51feeade51f +ecade51feaade51fe8ade51fe6ade51fe4ade51fe2ade51f0eeedfade51f33d9 +926833d10b41090064e1122931d120e22221136230d1226020c8018f1041fa8b +2ed00b40090029d182212ad120e2222103619c7102e2263129011c31d8ad1f1f +03a0081f01a0081f081f25d1126425d125d22363136600e020e7336528354075 +5026017600231047f78f01731cd31fd77223dc7300e772231dd7122704772227 +047710e662271997722323651075136000eee71f01e808a010e3d6af081fd4af +081fd2af081f1043248924666c6604677c677036f789f6af871f800111540900 +4440018c2c05018c4005018cc8805fa00c06018c00695fa080841e003c06018c +4000a0ff9040018c7040018c018200001000a0ff6ed220622822038901922302 +0900d6166bd2226210711032228df75ee82e018f03eee71f67d268d112220171 +111267d100e332213111321133113411351164d732275fd32223136504731223 +047304e66223a893322710715bd004e813a004e35cd112615031d98ff75e5bd1 +12611821d48ff75e56d1126002c8d38fe82eceaf090010430789f75ee82efa8f +56660036f789f6af871f4dd334533823048ff75e47d3326330322589e82e2a8d +05ee00e247d122212471422148d149d33b2149d3222349d249d7222732632033 +1b8d00e244d3222341d3222345d222213263203301ee248fea1f126241d11032 +1b8ff3611fa0787137d222621032d58ff75ed7af00e2d4afe71f726337d22033 +ffe22a622a1f00e233d3222330d3222334d2222132632033df89fa5eeb610271 +1a1ff3617871399221213890118158e012810fe0138133901481329015812ad1 +00e220211871a0e2202128d228d1206008c8018f1041fa8b24d1106008c81a8d +09eef362787223d306e1256771231041fb8b21d10fe2222120d1139222210471 +f072222100e11ed30d940d950d961dd80c994aa000e2ecafe61f314420010002 +000383001f0fff0077772c01a10f09008040018c1000a0ff6840018c0100feca +5040018c1c00a0ff1400a0ff1800a0ff4c40018c000000e0000014a50df0ad0b +000014e52143341284705fa018705fa0a086010080705fa0002870a0402070a0 +000081a04b1714060a071360783001400c37f5e00d4797071a0013670837d59e +ec3771230172027361714632ec8f85315123017202736032f78f617100e16cd2 +1222047212220472c193322204721fe332220472bc9332220472122204721222 +0472122263d1b4922221047160e2222161d100e0ae9500e6ad97ad935fd25630 +d62f019d230df66da014612101707030f58f027100e15ad21222047212220472 +9993322204721fe3322204729093322204721222047212220472122204728c93 +3222047248e3322288924ed32223047322230473222304732223047322234ad2 +12220472122249d17992222148d600e7769048d47598759373620922236106e5 +5d412c311c312c3145310a021365283501455c321942094287021a022831629e +ec31112601771043e68f027600e13ad212220472122204724d93322204721fe3 +322204724493322204724d933222047212220472122231d13b922221047160e2 +2221d87141922221f65007e11d401fcb40e2fc32061200eeeb1fee1fed1fffee +ec1f00eee91fdea700e8188d5a884d8f648823d12a9222210190230083602813 +20d10b4102e407a209001ed10b4103e402a209001bd10b4104e4fda109001ad1 +15922221019023008360fc1230f800011f3c1f0fff07001070170f0f00190242 +ff0fcf07011019fc00780342e003e07f040070a0240070a0000082a090e80000 +840070a0ac0070a0c00070a0800070a0000083a06b6f4606040170a0240170a0 +1c985fa0e405018c10905fa002890c911038138b06d107d2222183606488178b +05d10392222113a00900c8001f7c0900000070a001c200001c9c5fa0e6911038 +0389019023008360481273d173d2222173d174dc122c74d1126340e2fc323312 +72dee2644412c265551201e2222120e1122e6fd1122c6fd16fd612266fd71227 +6fd170d0122070d1141f131f00e9921f911f6ed2222f6eddb99700e66dd50b4d +00e46ddbb41fb31f921f911f6bd1122f6bd700e667d50b4d00e46ada922a6ad3 +92236ad492246ad592256ad692266ad792276ad092206ad19221830affe369d2 +322210e1122e68d1122c68d152d4122452d5122552d753d67226b41fb31f921f +911f52dee22f62d700e652d50b4d00e4922a55d0922055d1922155d2922255d3 +922355d4922455d5922555d69226830affee54d7e22757d112611821018b64a1 +01e255d1126177e22631028f017183a1090051d2122251d112600820018be9a1 +090009e11630018b2ca20a8801704bd102212dd14ce0fe044221047150e0fe05 +5221147154e0fe066221087144d22221047144d2222144d111600d6043d11161 +1f88028f1d6124a2090041d641d240e1236740e361271043fc8f027728901041 +f68f0c323cd112611821028f02e222a2090039d122211c911638290237d1088f +222115d136d2222114911038018b35d1106114d112d2222130d1126118210189 +61a2ffe713de30dc60a0c41fa00000800001030106010900000070a001820000 +0000200060805fa048805fa04c805fa00000700100007f0068805fa06c805fa0 +0000008044815fa0000000430000003f4c0d018c00008020000080429a99193f +ff0000ff000000e0040000e0080000e00c0000e0100000e0140000e0180000e0 +1c0000e014805fa00000740100003f0000ff00ff2480018c2080018c1880018c +00007f020000df01140a70a4c80a70a400f80000804070a42c80018c3080018c +0000c700010071a00000dc4268d1131f68dbb21f68ddd11f68daa22f68d9ffe7 +00e6c3940b49e365c41f66d1131fb21f65d1111fa22f65d765d5b7940b4900e6 +c41f64d1131fb21f63d1111fa22fffe762d6ac940b49e36561d1141f61d1131f +61d1121fd11fa22fffe700e65fd50b4900e45fd1141f5fd1131f5fd1121f5fd1 +111fa22fffe75ed65ed50b4908e45ed15ed222215ed25fd332220472e2220472 +00e3322204725cd00222047202220472022204720222830100e04ddd57dc1363 +53db57da3dd957d400e257d513a204e11638018b87a7836054d155d2222155d1 +ffe2222154d10b41090054d110600fc90688018b78a5090051d1126118211f89 +50d1126118211b8f0820028d09e1dca501e240e3fc331e131f134bd1126004e7 +00e600e5f3640b40787478e0fe011821028d01e245d1222145d2226101711222 +57a5836043d124ae090043d1222143d2222c36d136d22221b41fb31f921f911f +16d1122f3ed700e63ed50b4d00e4922a27d29222047292220472922204729222 +047292220472922204729222830a29d1ffe222217fae090033d111611d6133d0 +0031f78932d101e275ae2221800088000000004300008c42000000420000003f +4c0d018c0000304300001043ffffff8000009080000090430000404300020030 +00005c420000c0420000f0410000002000002f43000096430000fa4200007a43 +00000e0c24008020000000e020000080040000e0000000c00000803f00004343 +0c0000e0100000e0180000e044815fa00000008014805fa08c05018c8c705fa0 +1080018c0880018cbc00008c0080018c0c80018c000020002480018c00007801 +ff00ffff00008020280a78a41ff800001c80018c6edaa26c6ed1cb21122a6ed1 +10e2222114716dd222216dd16dd21222047212226cd16dd222216dde6ddbb41f +6dd1131f00e9921f911f6cd1122f6cddba9700e6e3650b4d00e4b41fb31f921f +911f68d1122f68d700e6e3650b4d00e466d1922166d292220472922204729222 +04729222047292220472922204729222830160d1ffe22221c22a5fd101e22221 +d7ad0900fc8b5dd111600d60407111611f88078d1d615ad101e2222156d10be2 +c7ad222181922c3156d33821f689f2af09007a977c3153dee821068b50d11261 +1821058950d6d1ad09004fd6cead09006c96cbad09004dd1126001701088508f +02214bda01ecc22a33dd10e1122d49de49d1122e32d133d2122248d3122332d1 +32d2222133d1141f131f00e9921f911f32d1122f32db4a9700e62dd50b4b00e4 +3fd1141f131f921f911f2ed1122f3dd700e627d50b4b00e42cd192212cd29222 +047292220472922204729222047292220472922204729222830126d1ffe22221 +4ce0fe04422a50e0fe05522d54e0fe06622e2dd113d712272cd10c7e122e2cd1 +c22188ad09002bd1141f2bd1131f2bd1121f2bd1111f2bd1122f00e60cd510d1 +0b4100e48ead090000802004e00700fff4805fa0000001004c805fa000007c01 +00003f0068805fa044815fa0000000800000802000008042000000430000003f +4c0d018c9a99193fff0000ff000000e0040000e014805fa01880018c200a7ca4 +1c80018cdfff000000f800002880018c48805fa060805fa0000060016c805fa0 +00000042ffff00ff00007f020000df012c80018c0000484300809f4300007042 +000052436666663f29071ceeed473b27722340e7fc377357722b93d76227922a +22240c77222791dee22508772227830310410d8d017002c840e6fc36d3160089 +c31601c88ad6df8d03888ad6ddaf290788daa41f88d1131f85d9921f87d1111f +87dee22fffe787d687d588d00b4008e4a41f87d1131f921f86d1111fe22fffe7 +85d686d581d10b4108e485da85d4422a85dd86d2222d74db85d1122b85dc86d6 +622c00e985d3922385d5922585d7922785d09220830ab22a84d1122d84d7722b +e22c92239225ffe2087322239220830ab22a122d7fd3322be22cfc7592257ed1 +f87012200875222508709220830ab22a7ad0022d722be22cf875122504759225 +0475222504759225830a75d5522a022d322be22c69d012200470122004702220 +04709220830a422a60d4422d6dd4422b622c62d69226f870922062d492240c76 +9226830ab22a68d6622d722be22cfc709220fc7492240870222008749224830a +b22a622d322be22c54d69226fc701220fc7422240c769226830ab22a5bd6622d +722be22c4dd712279220222408709220830a522a622d322be22c122748d31223 +22249220830a52d2241f131f621f51d1111fe22fffe739d5559434de0b4e00e6 +4dd1122a39d0022d34d1122b4bd1122c32d33ad2322230d539d4522439d69226 +39d79227830a00e300e2c36b636affe9736426a004e542d01cd142dc04a01366 +23671ad140dc236640dddb2724dd722d25de022e14d0c22019d7722b27dc122c +27d76227922a2224830d122d622e9220222b222c2227222a2224830d1045678d +017302e1336002c8028d392193a401c801c8018950a4038829d01821d08f1367 +29dcd1af13668000080000e00000803f0000574300006b430000f04100002041 +0000003f00c00034002080204c0d018c00005c4200000c4200c0003600008020 +000000e008000080040000e0000000c0002480200c0000e000c80088100000e0 +140000e0180000e01c0000e00000f0420000e041000050420000004300002043 +080000f00004802000002443000040430000604300008042c800008000020030 +0000484300006c4300005643c80000e0add112600288018b44a1ffe7abda00e9 +922aabdd922dabdc922cabde922eabd09220abd19221abd29222abd39223830a +aad5abdbb11fabd1122fabdeabd753660b4e00e4b11faad1122faad7aad6abd5 +0b4e00e4aad1122a922d922c9ad4922402e29ad5222506e199d6122699d72227 +99d01220830ab11fa0d1a2d7a2d6a3d5a3d40b4e122f922a922d922c8ed29222 +8ed392238ed492248ed592258ed69226830a9cd7741f9cd1131f9cdee21f9cd0 +011f9cdee22f9cdb9cd69dd59dd40b4bffe794d1141f9cd1131f94d2221f9bd1 +111fe22f9ad696d596d40b4bffe7922a922d922c78d3922378d4922478d59225 +78d6922678d79227830a92d0041f88d1131f91d2221f91d3311f91d4422f91d7 +91d592d40b4b00e68ad5541f90d6631f89d7721f8fd0011f8fd1122f8fd78ad5 +8ad40b4b00e6922a922d922c62d2922262d3922362d4922462d5922562d69226 +830a87d187d2222187d1141f87d1131f87d1121f87d1111f87d1122f87d700e6 +87d50b4b00e460d7741f86d1131f78d0021f85d1111fe22f84d785d591940b4b +00e6922a922d922c4bd192214bd292224bd392234bd492244bd59225830a7dd6 +622f7dde7dd77ed67ed57fd40b4e090078d7722f7dd77ed67ed57fd40b4e0900 +49dee41f52d0031f61d1121f5bd2211f5bd3322f5bd75cd55cd40b4b00e6e41f +5bd4431f421f5bd5511f5bd6622f5bd755d556d40b4b00e6922a922d922c2ed7 +92272ede922e2ed092202ed192212ed29222830a69d1ffe22221439110380189 +70ab090066d267d1226001c8018d1041fa8b65d165d2222165d166d222210471 +65d22221047165d2222104713fd22221047163d2222163d363d220e122230172 +1041fb8f047361d161d22221147101e22221136253d1226001c8018d1041fa8b +43ab09005bd1141f131f5bd1121f111f5ad1122f5ad65bd51fd10b4108e4adae +09008000960009002c80018c000000e0040000e0080000e00c0000e0100000e0 +140000e0180000e01c0000e00000a0420000403f0000c8421c0c018c00007043 +00006f43000074430000524300005c4300000020000000430000444300008042 +0000020000005c42000068430000f041000048430000003f4c0d018c00c40030 +000090200800000400008d4300007a4300c6003000002a430000024300000c43 +9a99193fff0000800000909400000002000034430000f0429a99993e0000ff80 +60815fa0000000800000d84200008c420000be42000020423333333fffff00ff +000080200000964300008043ffffff80000090806666663ffc0c018c0000d442 +000082420000c2420000344200004d4300009143000039430000874314805fa0 +186c5fa080841e0000e100ac7856341200e000ac2200008000e1000c0c010022 +0000006514e000ac0001dec0046c5fa000e0000c00008a420000a0403333733f +00000c0c1b00802063d222610171122283603c88028f4288a9a00900048ffb5e +e82e018ba8a009005cd10b4109005cd10b4110e45bd1126283639c73aa911633 +028ff951c6a009002831fc5e163e008b1c1ffd5e123e00891d1ffe5e1c3eee1f +291f00e148e0fe022822038d1821018922a10900028d3fe112a1fd521638018b +c2a08360017883603c88028f3ce131a809001638018b18a846881e88028f2d88 +1ea80900028f0f881fa8090001899fa183603dd10b4101e4836208422c320842 +8c32184209428c326591192283603fc9036108410c31184134d33c3134d32121 +1043fc8f02710daa090032d1222100e131d210220c720ae33022047210222fd1 +a0e220212ed22fd1206008c8018f1041fa8b2bd1106008c8238d13e268712121 +00e221210ae3312121212121212124d224d1206008c8018f1041fa8b20d11060 +08c811896871116211601162116211611ad220620fc918401c610b211ad257af +12221ad1faaf090018d1f7af090018d00b4000e404a00b1f15d00b4001e40b1f +06d10b41090006d10b4110e405d1126267af291f9500ff7f1080018c0c06018c +4c06018c0c00d8ffe405018c000000a500a000000880018c84705fa09c705fa0 +18705fa0a08601000480018cffff0000a007018caa9110381f8ff55ee82e1c89 +56d1a493322156d1ec31291f55d2122255d112611821078d8360226253d11032 +038f01c8c2a00ae201c8018be1a009004fd150d2122236af017887911038b78f +ce71fd53fc5ee8331633268dfe5e4ad3ec337d911633268d44e0fe000820278d +f65e3ed175933221291ffe5e42d2ec326e9116325e8d44e0fe0008205d8ff65e +e82e5d8f48e0fe0008205c8fff5ee82e278df85039d1b9afec3130d15a933221 +37d1b3af291f2dd155933221ddaf291fe82e0e8d58e029d1fe000221291ffd52 +fc5ee832449116322dd19f8dfe5ecdaf0900291fd6ae01e120d13d922221efaf +fd520820038df75e26d18faf0c31e82e028925d18aafec31fa5ee82e028923d1 +84afec31fb5ee82e038d40e020d17dafec3101e1fe021732038f40e01dd175af +2c31fe000188028910d16faf09001ad16caf090019d169af090019d166af0900 +18d163af090018d160af0900fa00e003c8001f7c007cff03007e090010905fa0 +0001d0ba4840018c1080018c0d600d600000400050805fa043c721fe0008d0ba +0200d0ba0009d0ba000ad0ba000bd0ba000dd0ba0007d0ba4d4d0d600300d0ba +0400d0ba0500d0ba0600d0ba5bd11261c971263112895ad112611821138959d1 +1262a9911032028957d108a0090057d1126118210a8956d101a0090055d156d2 +122221af836055d1f9af090054d1f6af090054d11daf090029071ce61821028d +6d4778ab090050d050dc51d67cab090003c9038801897ba83fe13fe11638028d +83605aae836201c8018baba8090049d1019c230c09009ced47d2226210712031 +02ee068fe71f45d11261503102ee048de71f01922302090020e9019123010900 +5ee93360038829071ce11d473cd038d136dc49ab1366f48f182139d06eab0900 +4121017002715630058f20c8d62f019d230df66d56ebf38f2364f1af336401a0 +93628362212101700271a630f78f0388f78d46302c8f66302c8b312101705888 +f38f027127d12892222127d227d1292127d33b2113662367e07726d518e35227 +1043fc8f047724d30ce77223fc7310e7722322d708e3722101771043fb8f0471 +83061ed700e101e60195230508e318dec8af5362c6af7362280680000c80018c +0880018c0480018c030cd0ba0080018c040cd0ba010cd0ba4840018c020cd0ba +9d5a0d60000020000000484300006c430000803f000040001000a0ff1400a0ff +000070431c815fa0e040018ce0ffff03000000e0111111113c0000ffe0ffc000 +400000006400000000000000400100000e00000000000000100000000a000000 +0000000001000400000004000300040001000c00010024000100040001000000 +0200000000010000000200000303000000017f80ff0000000000000001000000 +02000000ffffff7f0000008001000080ffffffff78563412efbeadde00000100 +5345474120534547414b4154414e4120000000000000000017b7d13800000000 +00000000ffffffff000020440000000017b7d1380000803f00000000ffffffff +000000000000f04317b7d138000000000000803fffffffff +""") + # The same program built with -DLIVE_MMU_ALIGN: its MMU test also asks for # the address errors a misaligned access raises (live_prog.c). For a core # that keeps translations in a table, which they all do: it is the build @@ -3901,7 +4316,7 @@ def main(): if len(sys.argv) == 2 and sys.argv[1].endswith('.elf'): write_elf(sys.argv[1]) return 0 - if len(sys.argv) not in (2, 3) or sys.argv[2:] not in ([], ['--no-region-array'], ['--mmu-align'], ['--slant'], ['--hscale'], ['--cull'], ['--atlas'], ['--dma'], ['--clip']): + if len(sys.argv) not in (2, 3) or sys.argv[2:] not in ([], ['--no-region-array'], ['--mmu-align'], ['--slant'], ['--hscale'], ['--cull'], ['--atlas'], ['--dma'], ['--clip'], ['--keep']): sys.stderr.write(__doc__) return 2 out = sys.argv[1] @@ -3922,6 +4337,8 @@ def main(): program = PROGRAM_DMA elif sys.argv[2:] == ['--clip']: program = PROGRAM_CLIP + elif sys.argv[2:] == ['--keep']: + program = PROGRAM_KEEP d = os.path.dirname(out) or '.' ip = bytearray(16 * 2048) diff --git a/tools/threads/live_prog.c b/tools/threads/live_prog.c index 696c98bf..afe75e3c 100644 --- a/tools/threads/live_prog.c +++ b/tools/threads/live_prog.c @@ -29,7 +29,8 @@ * for PROGRAM_MMU_ALIGN, with -DLIVE_SLANT for PROGRAM_SLANT, and with * -DLIVE_SLANT -DLIVE_HSCALE for PROGRAM_HSCALE, with -DLIVE_CULL for * PROGRAM_CULL, with -DLIVE_ATLAS for PROGRAM_ATLAS, with -DLIVE_DMA - * for PROGRAM_DMA, and with -DLIVE_CLIP for PROGRAM_CLIP. */ + * for PROGRAM_DMA, with -DLIVE_CLIP for PROGRAM_CLIP, and with -DLIVE_KEEP + * for PROGRAM_KEEP. */ typedef unsigned int u32; typedef unsigned short u16; #define PVR(off) (*(volatile u32 *)(0xA05F8000 + (off))) @@ -713,6 +714,18 @@ static void ta_atlas(void) * which is drawn nearest of all over most of the right, never appears. */ static u32 scene_in_part; +#ifdef LIVE_KEEP +/* With -DLIVE_KEEP: a picture rendered to a texture once, at the very end + * of a frame, and from the next frame on only drawn with - never read or + * written by the program. Nothing in that frame makes the core put it in + * video memory, so a state saved right after it is saved while it is + * still on its way; loaded, the state has to have it all the same. + * 64 by 64, yellow with its top left quarter cyan, drawn in the screen's + * top left. */ +#define KEEP_ADDRESS 0x600000 +static u32 keep_done, keep_age; +#endif + static void ta_scene(void) { /* The screen is 320 by 240 in the video mode the program is started in. */ @@ -815,6 +828,13 @@ static void ta_scene(void) #endif #ifdef LIVE_CLIP ta_clipped(); +#endif +#ifdef LIVE_KEEP + /* the kept picture, from the frame after the one it was rendered in: + * RGB565, not twiddled, 64 by 64 */ + if (keep_done == 2) + ta_quad(TA_TEXTURED, TSP_PLAIN | (3u << 3) | 3u, (1u << 27) | (1u << 26) | (KEEP_ADDRESS >> 3), 0xFFFFFFFF, + F(0.95f), F(5.0f), F(5.0f), F(69.0f), F(69.0f)); #endif ta_send(0, 0, 0, 0, 0, 0, 0, 0); /* end of the opaque list */ @@ -985,6 +1005,29 @@ static void render_to_texture(void) for (x = 64; x < 128; x++) tex[y * 128 + x] = mark; } +#ifdef LIVE_KEEP + if (keep_done) + keep_done = 2; + else if (++keep_age == 16) + { + PVR(0x48) = 1; /* FB_W_CTRL: RGB565 */ + PVR(0x4C) = 64 * 2 / 8; + PVR(0x60) = 0x01000000 | KEEP_ADDRESS; + PVR(0x68) = 63 << 16; + PVR(0x6C) = 63 << 16; + PVR(0x144) = 0x80000000; /* TA_LIST_INIT */ + ta_quad(0, TSP_PLAIN, 0, 0xFFFFFF00, F(0.5f), F(0.0f), F(0.0f), F(64.0f), F(64.0f)); + ta_quad(0, TSP_PLAIN, 0, 0xFF00FFFF, F(0.6f), F(0.0f), F(0.0f), F(32.0f), F(32.0f)); + ta_send(0, 0, 0, 0, 0, 0, 0, 0); + PVR(0x14) = 0xFFFFFFFF; /* STARTRENDER */ + PVR(0x48) = fb_w_ctrl; + PVR(0x4C) = linestride; + PVR(0x60) = fb_w_sof1; + PVR(0x68) = 639 << 16; + PVR(0x6C) = 479 << 16; + keep_done = 1; + } +#endif } /* A second render pass, drawn over the first: the lists are opened again