States: no copy from a chip the machine lacks; run-ahead tested

A state saved or loaded copied the Dreamcast's SRAM, which it does not
have: no memory and a size of 0, a copy from or to a null pointer
(UBSan). Parts of no size are now nothing to copy.

headless.sh's run-ahead stage passes: its do-nothing OpenGL read back
from the mapping the core writes through, so what it fetched off the
card depended on the core's history, and frames run from a state loaded
were not the frames run straight. Readbacks are now the same bytes every
time. headless.sh runs in CI (Headless).

gl_frames runs frames ahead (GLF_AHEAD), and tools/gl/gl_ahead.sh runs a
disc (live_disc.py --keep) that renders to a texture at the very end of
a frame and only draws with it after: every frame run ahead has to show
what a plain run shows, the picture saved in the state while still on
the card. On OpenGL and OpenGL ES, and under ASan and UBSan in CI.
This commit is contained in:
libretroadmin committed 2026-11-01 20:56:40 +01:00
1 parent 68a575a60d
commit a864367e52
10 files changed
+712 -9

No files matched your search

+43
View File
@@ -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
+9 -1
View File
@@ -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
+4
View File
@@ -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)
+5 -2
View File
@@ -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);
+60
View File
@@ -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())
+74
View File
@@ -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.<pid>.
# 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"
+35 -1
View File
@@ -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;
}
+19 -2
View File
@@ -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 <stdlib.h>')
print('#include <string.h>')
print(SPECIAL)
for name in names:
if not name.startswith('gl') or name in special:
+419 -2
View File
@@ -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)
+44 -1
View File
@@ -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