From ef67996a735577a52d869b15ff745c41e765b41d Mon Sep 17 00:00:00 2001 From: jonnosan Date: Mon, 24 Aug 2009 10:20:21 +0000 Subject: [PATCH] 0.9.29 git-svn-id: http://svn.code.sf.net/p/netboot65/code@187 93682198-c243-4bdb-bd91-e943c89aac3b --- client/cfg/c64_16kcart.cfg | 2 +- client/drivers/c64_disk_access.s | 67 ++++++++++- client/examples/Makefile | 13 ++- client/examples/upnatom.s | 46 ++++++-- client/examples/url.cfg | 1 + client/inc/nb65_constants.i | 12 ++ client/inc/version.i | 2 +- client/ip65/function_dispatcher.s | 51 +++++++- client/ip65/url.s | 6 +- client/nb65/nb65_c64.s | 167 ++++++++++++++++++++++----- client/tcp_test.bat | 2 +- client/test/Makefile | 6 +- client/test/test_disk_io.s | 138 +++++++++++++--------- client/test/test_parser.s | 13 ++- dist/make_dist.rb | 2 +- dist/version_number.txt | 2 +- doc/README.C64.html | 13 ++- doc/nb65_api_technical_reference.doc | Bin 177664 -> 188928 bytes 18 files changed, 424 insertions(+), 119 deletions(-) create mode 100644 client/examples/url.cfg diff --git a/client/cfg/c64_16kcart.cfg b/client/cfg/c64_16kcart.cfg index b3dc4bf..e0d661e 100644 --- a/client/cfg/c64_16kcart.cfg +++ b/client/cfg/c64_16kcart.cfg @@ -1,7 +1,7 @@ # CA65 config for a 16KB cart MEMORY { - IP65ZP: start = $A3, size = $13, type = rw, define = yes; + IP65ZP: start = $20, size = $13, type = rw, define = yes; #this cart replaces BASIC so ok to use that space HEADER: start = $8000, size = $18, file = %O; DEFAULTS: start = $8018, size = $1E, file = %O; ROM: start = $8036, size = $3FC8, define = yes, file = %O; diff --git a/client/drivers/c64_disk_access.s b/client/drivers/c64_disk_access.s index 53a88f4..fd79bc2 100644 --- a/client/drivers/c64_disk_access.s +++ b/client/drivers/c64_disk_access.s @@ -14,15 +14,17 @@ .export io_read_sector .export io_read_catalogue .export io_read_catalogue_ex - +.export io_read_file .export io_read_file_with_callback .export io_filename +.export io_filesize +.export io_load_address .export io_callback .importzp copy_src .import ip65_error .import output_buffer -;.importzp copy_dest +.importzp copy_dest ;reuse the copy_src zero page location buffer_ptr = copy_src @@ -45,6 +47,8 @@ SETLFS = $ffba io_sector_no: .res 1 io_device_no: .byte 0 io_filename: .res 2 + io_filesize: .res 2 ;although a file on disk can be >64K, io_filesize is only used when loading into RAM hence file must be <64K + io_load_address: .res 2 error_buffer = output_buffer + 256 command_buffer = error_buffer+128 sector_buffer_address: .res 2 @@ -71,6 +75,63 @@ tmp_buffer_ptr=write_byte_to_buffer+1 .code +;routine to read a file +; inputs: +; io_device_number - specifies drive to use ($00 = same as last time, $01 = first disk (i.e. #8), $02 = 2nd disk (drive #9)) +; io_filename - specifies filename to open +; AX - address of buffer to read file into (set to $0000 to treat first 2 bytes as load address) +; outputs: +; on errror, carry flag is set +; otherwise, io_filesize will be set to size of file and io_load_address will be set to actual load address used. +; +io_read_file: + stax io_load_address + sta sector_buffer_address + stx sector_buffer_address+1 ;this also sets the Z flag + bne @sector_buffer_address_set + ;if we get here, X was $00 so we need to use first 2 bytes of file as load address + ldax #output_buffer + stax sector_buffer_address + +@sector_buffer_address_set: + ldax #read_file_callback + stax io_callback + lda #0 + sta io_filesize + sta io_filesize+1 + ldax sector_buffer_address + jsr io_read_file_with_callback + rts + +read_file_callback: + sty io_filesize ;only 1 (the last) sector can ever be !=$100 bytes + bne @not_full_sector + inc io_filesize+1 + inc sector_buffer_address +1 +@not_full_sector: + lda io_load_address+1 ;is the high byte of the address $00? + bne @done + ldax output_buffer ;if we get here we must have used downloaded into the static output buffer, so the + ;first 2 bytes there are the real load address + stax copy_dest ;now copy the rest of the sector + stax sector_buffer_address + stax io_load_address + dey + dey +@copy_one_byte: + dey + lda output_buffer+2,y + sta (copy_dest),y + inc sector_buffer_address + bne :+ + inc sector_buffer_address+1 +: + tya + bne @copy_one_byte + +@done: + rts + ;routine to read a file with a callback after each 256 byte sector ; inputs: ; io_device_number - specifies drive to use ($00 = same as last time, $01 = first disk (i.e. #8), $02 = 2nd disk (drive #9)) @@ -132,8 +193,8 @@ io_read_file_with_callback: sta (buffer_ptr),y inc buffer_counter bne @get_next_byte - ldy #$00;= 256 bytes + jsr jmp_to_callback jmp @get_next_sector diff --git a/client/examples/Makefile b/client/examples/Makefile index 6663bd4..f456e0f 100644 --- a/client/examples/Makefile +++ b/client/examples/Makefile @@ -13,8 +13,7 @@ INCFILES=\ ../inc/commonprint.i\ ../inc/net.i\ -all: \ - upnatom.prg \ +all: upnatom.prg upnatom.d64 %.o: %.s $(AS) $(AFLAGS) $< @@ -27,9 +26,15 @@ sine_data.i: make_sine_data.rb upnatom.prg: upnatom.o nt2play.o $(IP65TCPLIB) $(C64PROGLIB) $(INCFILES) ../cfg/c64prg.cfg $(LD) -m upnatom.map -vm -C ../cfg/c64prg.cfg -o upnatom.prg $(AFLAGS) $< $(IP65TCPLIB) $(C64PROGLIB) $(NT2PLAY) - cp upnatom.prg ../../server/boot/ + cp upnatom.prg ../../server/boot/ + +upnatom.d64: upnatom.prg url.cfg + cp upnatom.prg autoexec.prg + ripxplore.rb --init CbmDos upnatom.d64 -a autoexec.prg + ripxplore.rb upnatom.d64 -a url.cfg -t C64Seq + clean: - rm -f *.o *.pg2 *.prg *.map + rm -f *.o *.pg2 *.prg *.map upnatom.d64 distclean: clean rm -f *~ diff --git a/client/examples/upnatom.s b/client/examples/upnatom.s index efdd8e9..a285f20 100644 --- a/client/examples/upnatom.s +++ b/client/examples/upnatom.s @@ -139,9 +139,19 @@ init: @init_ok: ;if we got here, we have found the NB65 API and initialised the IP stack - - jsr setup_static_scroll_text +;try and load the config file + ldax #read_url_file_param_buffer + nb65call #NB65_FILE_LOAD + bcs @use_default_url + clc + lda #0 + ldy read_url_file_param_buffer+NB65_FILE_ACCESS_FILESIZE + sta feed_url,y ;put a zero at the end of the URL + +@use_default_url: + jsr setup_static_scroll_text + lda #0 jsr clear_screen lda #DARK_GRAY @@ -243,9 +253,12 @@ init: jsr reset_input_buffer + lda #0 + sta scroll_buffer_1 ;set this buffer to be an empty string, in case we try to scroll it + ;now download the feed @download_feed: - + ldax #feed_url stax nb65_param_buffer+NB65_URL ldax #download_buffer @@ -334,7 +347,7 @@ setup_sprites: tay lda sprite_x_pos,x sta $d000,y ;sprite 0 X pos (LSB) - lda 0 + lda #0 sta $d001,y ;sprite 0 Y pos ;colour sprite 0 @@ -534,6 +547,7 @@ setup_static_scroll_text: stax current_input_ptr @next_byte: jsr get_a + beq @next_byte cmp #'%' beq @operator pha @@ -564,7 +578,8 @@ setup_static_scroll_text: ldy #0 @copy_feed_url_loop: lda feed_url,y - beq @next_byte + cmp #$20 + bcc @next_byte ;any control char (including CR,LF, and $00) should be treated as end of URL jsr emit_a iny bne @copy_feed_url_loop @@ -840,10 +855,11 @@ sprite_x_msb: .byte $80 sprite_y_pos: -.include "sine_data.i" .repeat 128 .byte 0 .endrep +.include "sine_data.i" + ;.include "sine_data.i" @@ -859,8 +875,22 @@ scroll_template: feed_url: -;.byte "http://search.twitter.com/search.atom?q=kipper",0 -.byte "http://static.cricinfo.com/rss/livescores.xml",0 +.byte "http://search.twitter.com/search.atom?q=kipper",0 +;leave space for whatever we read in from disk +.repeat 128 +.byte 0 +.endrep + +url_config_file: + +.byte "URL.CFG",0 + +read_url_file_param_buffer: + .word url_config_file ;NB65_FILE_ACCESS_FILENAME + .word feed_url ;B65_FILE_ACCESS_POINTER + .word $0000 ;NB65_FILE_ACCESS_FILESIZE - should be filled in + .byte $00 ;NB65_FILE_ACCESS_DEVICE + title: .byte "",0 diff --git a/client/examples/url.cfg b/client/examples/url.cfg new file mode 100644 index 0000000..8fa7f64 --- /dev/null +++ b/client/examples/url.cfg @@ -0,0 +1 @@ +http://static.cricinfo.com/rss/livescores.xml \ No newline at end of file diff --git a/client/inc/nb65_constants.i b/client/inc/nb65_constants.i index 0573ce4..a4bfc4f 100644 --- a/client/inc/nb65_constants.i +++ b/client/inc/nb65_constants.i @@ -48,6 +48,8 @@ NB65_DNS_RESOLVE EQU $30 ;inputs: AX points to a DNS parameter str NB65_DOWNLOAD_RESOURCE EQU $31 ;inputs: AX points to a URL download structure, outputs: none NB65_PING_HOST EQU $32 ;inputs: AX points to destination IP address for ping, outputs: AX=time (in milliseconds) to get response +NB65_FILE_LOAD EQU $40 ;inputs: AX points to a file access parameter structure, outputs: none + NB65_PRINT_ASCIIZ EQU $80 ;inputs: AX=pointer to null terminated string to be printed to screen, outputs: none NB65_PRINT_HEX EQU $81 ;inputs: A=byte digit to be displayed on screen as (zero padded) hex digit, outputs: none NB65_PRINT_DOTTED_QUAD EQU $82 ;inputs: AX=pointer to 4 bytes that will be displayed as a decimal dotted quad (e.g. 192.168.1.1) @@ -83,6 +85,7 @@ NB65_TFTP_FILESIZE EQU $04 ;2 byte file length (filled in ;offsets in TFTP Server parameter structure (used by NB65_TFTP_SET_SERVER) NB65_TFTP_SERVER_IP EQU $00 ;4 byte IP address of TFTP server + ;offsets in DNS parameter structure (used by NB65_DNS_RESOLVE) NB65_DNS_HOSTNAME EQU $00 ;2 byte pointer to asciiz hostname to resolve (can also be a dotted quad string) NB65_DNS_HOSTNAME_IP EQU $00 ;4 byte IP address (filled in on succesful resolution of hostname) @@ -106,6 +109,8 @@ NB65_TCP_CALLBACK EQU $06 ;2 byte address of routine NB65_TCP_PAYLOAD_LENGTH EQU $00 ;2 byte length of payload of packet (after all ethernet,IP,UDP/TCP headers) NB65_TCP_PAYLOAD_POINTER EQU $02 ;2 byte pointer to payload of packet (after all headers) + + ;offsets in TCP/UDP packet parameter structure NB65_REMOTE_IP EQU $00 ;4 byte IP address of remote machine (src of inbound packets, dest of outbound packets) NB65_REMOTE_PORT EQU $04 ;2 byte port number of remote machine (src of inbound packets, dest of outbound packets) @@ -125,6 +130,13 @@ NB65_URL EQU $00 ;2 byte pointer to null te NB65_URL_DOWNLOAD_BUFFER EQU $02 ;2 byte pointer to buffer that resource specified by URL will be downloaded into NB65_URL_DOWNLOAD_BUFFER_LENGTH EQU $04 ;2 byte length of buffer (download will truncate when buffer is full) + +;offsets in file access parameter structure (used by NB65_FILE_LOAD) +NB65_FILE_ACCESS_FILENAME EQU $00 ;2 byte pointer to asciiz filename (or filemask) +NB65_FILE_ACCESS_POINTER EQU $02 ;2 byte pointer to memory location data to be stored in OR address of callback function +NB65_FILE_ACCESS_FILESIZE EQU $04 ;2 byte file length (filled in by NB65_FILE_ACCESS) +NB65_FILE_ACCESS_DEVICE EQU $06 ;1 byte device number (set to $00 to use last accessed device) + ;error codes (as returned by NB65_GET_LAST_ERROR) NB65_ERROR_PORT_IN_USE EQU $80 NB65_ERROR_TIMEOUT_ON_RECEIVE EQU $81 diff --git a/client/inc/version.i b/client/inc/version.i index 6e356b6..7189cc2 100644 --- a/client/inc/version.i +++ b/client/inc/version.i @@ -1 +1 @@ -.byte "0.9.25" +.byte "0.9.30" diff --git a/client/ip65/function_dispatcher.s b/client/ip65/function_dispatcher.s index db51f24..1fc86a8 100644 --- a/client/ip65/function_dispatcher.s +++ b/client/ip65/function_dispatcher.s @@ -62,7 +62,7 @@ ip_configured_flag: .code irq_handler: - jsr NB65_VBL_VECTOR + jsr NB65_VBL_VECTOR jmp jmp_old_irq @@ -473,7 +473,54 @@ ip_configured: jmp url_download : - + cpy #NB65_FILE_LOAD +bne :+ +.import io_device_no +.import io_read_file +.import io_filename +.import io_filesize +.import io_load_address + phax + ldy #NB65_FILE_ACCESS_FILENAME + lda (nb65_params),y + sta io_filename + iny + lda (nb65_params),y + sta io_filename+1 + + ldy #NB65_FILE_ACCESS_DEVICE + lda (nb65_params),y + sta io_device_no + + ldy #NB65_FILE_ACCESS_POINTER+1 + lda (nb65_params),y + tax + dey + lda (nb65_params),y + jsr io_read_file + plax + bcc @read_file_ok + rts + +@read_file_ok: + stax nb65_params + + ldy #NB65_FILE_ACCESS_POINTER + lda io_load_address + sta (nb65_params),y + iny + lda io_load_address+1 + sta (nb65_params),y + + ldy #NB65_FILE_ACCESS_FILESIZE + lda io_filesize + sta (nb65_params),y + iny + lda io_filesize+1 + sta (nb65_params),y + rts +: + cpy #NB65_PING_HOST .import icmp_echo_ip .import icmp_ping diff --git a/client/ip65/url.s b/client/ip65/url.s index d977566..a8e709c 100644 --- a/client/ip65/url.s +++ b/client/ip65/url.s @@ -76,6 +76,7 @@ selector_buffer=output_buffer ;parses a URL into a form that makes it easy to retrieve the specified resource ;inputs: ;AX = address of URL string +;any control character (i.e. <$20) is treated as 'end of string', e.g. a CR or LF, as well as $00 ;outputs: ; sec if a malformed url, otherwise: ; url_ip = ip address of host in url @@ -107,7 +108,7 @@ url_parse: cmp #'H' beq @http @exit_with_error: - lda #NB65_ERROR_MALFORMED_URL + lda #NB65_ERROR_MALFORMED_URL sta ip65_error @exit_with_sec: sec @@ -195,7 +196,8 @@ lda #url_type_gopher @copy_one_byte: ldy src_ptr lda (copy_src),y - beq @end_of_selector + cmp #$20 + bcc @end_of_selector ;any control char (including CR,LF, and $00) should be treated as end of URL inc src_ptr @save_first_byte_of_selector: ldy dest_ptr diff --git a/client/nb65/nb65_c64.s b/client/nb65/nb65_c64.s index 6ffddb0..467441d 100644 --- a/client/nb65/nb65_c64.s +++ b/client/nb65/nb65_c64.s @@ -111,8 +111,8 @@ .import __SELF_MODIFIED_CODE_SIZE__ .import cfg_tftp_server - tftp_dir_buffer = $6020 nb65_param_buffer = $6000 + directory_buffer = $6020 .data exit_cart: @@ -219,12 +219,14 @@ init: ldax #$2000 jsr copymem .endif - -ldax #init_msg + + ldax #netboot65_msg + jsr print + ldax #init_msg+1 jsr print nb65call #NB65_INITIALIZE - bcc main_menu + bcc init_ok print_failed jsr print_errorcode jsr wait_for_keypress @@ -239,6 +241,26 @@ print_main_menu: ldax #main_menu_msg jmp print +init_ok: +.if (BANKSWITCH_SUPPORT=$03) +;look for an 'autoexec' file + jsr print_cr + ldax #loading_msg + jsr print + ldax #autoexec_filename + stax io_filename + jsr print + jsr print_cr + ldax #$0000 + jsr io_read_file + bcs main_menu +@file_read_ok: + ldax #load_ok_msg + jsr print + ldax io_load_address + jmp boot_into_file +.endif + main_menu: jsr print_main_menu jsr print_ip_config @@ -250,6 +272,12 @@ main_menu: bne @not_tftp jmp @tftp_boot @not_tftp: +.if (BANKSWITCH_SUPPORT=$03) + cmp #KEYCODE_F2 + bne @not_disk + jmp disk_boot + @not_disk: +.endif cmp #KEYCODE_F3 .if (BANKSWITCH_SUPPORT=$03) @@ -441,17 +469,15 @@ cmp #KEYCODE_F7 jsr wait_for_keypress jsr @change_config - @tftp_boot: - ldax #tftp_dir_filemask @get_tftp_directory_listing: stax nb65_param_buffer+NB65_TFTP_FILENAME - ldax #tftp_dir_buffer + ldax #directory_buffer stax nb65_param_buffer+NB65_TFTP_POINTER ldax #getting_dir_listing_msg @@ -462,7 +488,7 @@ cmp #KEYCODE_F7 bcs @dir_failed - lda tftp_dir_buffer ;get the first byte that was downloaded + lda directory_buffer ;get the first byte that was downloaded bne :+ jmp @no_files_on_server : @@ -472,7 +498,7 @@ cmp #KEYCODE_F7 sta $d018 - ldax #tftp_dir_buffer + ldax #directory_buffer jsr select_option_from_menu bcc @tftp_filename_set @@ -513,7 +539,7 @@ cmp #KEYCODE_F7 @dir_failed: - ldax #tftp_dir_listing_fail_msg + ldax #dir_listing_fail_msg jsr print jsr print_errorcode jsr print_cr @@ -522,22 +548,27 @@ cmp #KEYCODE_F7 jmp @tftp_filename_set @no_files_on_server: - ldax #no_files_on_server + ldax #no_files jsr print jmp @tftp_boot_failed -@file_downloaded_ok: +@file_downloaded_ok: +ldax nb65_param_buffer+NB65_TFTP_POINTER +boot_into_file: + stax nb65_param_buffer ;use the param buffer as a temp holding place for the load address ;get ready to bank out nb65call #NB65_DEACTIVATE + + jsr $ffe7 ; make sure all files have been closed. ;check whether the file we just downloaded was a BASIC prg - lda nb65_param_buffer+NB65_TFTP_POINTER + lda nb65_param_buffer cmp #01 bne @not_a_basic_file - lda nb65_param_buffer+NB65_TFTP_POINTER+1 + lda nb65_param_buffer+1 cmp #$08 bne @not_a_basic_file @@ -545,6 +576,7 @@ cmp #KEYCODE_F7 lda $805 cmp #$9e ;opcode for 'SYS' bne @not_a_basic_stub + ldax #$806 ;should point to ascii string containing address that was to be SYSed jsr parse_integer jmp exit_cart_via_ax ;good luck! @@ -569,14 +601,75 @@ cmp #KEYCODE_F7 jmp exit_cart_via_ax .endif -@not_a_basic_file: - ldax nb65_param_buffer+NB65_TFTP_POINTER +@not_a_basic_file: + ldax nb65_param_buffer exit_cart_via_ax: sta call_downloaded_prg+1 stx call_downloaded_prg+2 jmp exit_cart .if (BANKSWITCH_SUPPORT=$03) +disk_boot: + .import io_read_catalogue + .import io_device_no + .import io_filename + .import io_read_file + .import io_load_address + lda #00 ;use default drive + sta io_device_no + + ldax #directory_buffer + jsr io_read_catalogue + + lda directory_buffer ;get the first byte that was downloaded + bne :+ + jmp @no_files_on_disk +: + + ;switch to lower case charset +; lda #23 +; sta $d018 + + + ldax #directory_buffer + + jsr select_option_from_menu + bcc @disk_filename_set + jmp main_menu + +@dir_failed: + ldax #dir_listing_fail_msg +@print_error: + jsr print + jsr print_errorcode + jsr print_cr + jmp @wait_keypress_then_return_to_main + +@no_files_on_disk: + ldax #no_files + jsr print +@wait_keypress_then_return_to_main: + jsr wait_for_keypress + jmp main_menu + +@disk_filename_set: + stax io_filename + ldax #loading_msg + jsr print + ldax io_filename + jsr print + jsr print_cr + ldax #$0000 + jsr io_read_file + bcc @file_read_ok + ldax #file_read_error + jmp @print_error +@file_read_ok: + ldax #load_ok_msg + jsr print + ldax io_load_address + jmp boot_into_file + net_apps_menu: jsr cls ldax #netboot65_msg @@ -706,24 +799,29 @@ exit_gopher: jsr print_a jmp net_apps_menu .endif - .rodata + + +.rodata netboot65_msg: .byte 13,"NB65 - V" .include "../inc/version.i" +.if (BANKSWITCH_SUPPORT=$03) +.byte " (TCP)" +.endif .byte 13,0 main_menu_msg: .byte 13,"MAIN MENU",13,13 -.byte "F1: TFTP BOOT" .if (BANKSWITCH_SUPPORT=$03) -.byte " F3: NET APPS" -.else -.byte " F3: BASIC" -.endif -.byte 13 +.byte "F1: TFTP BOOT F2: DISK BOOT",13 +.byte "F3: NET APPS F4: TBA",13 .byte "F5: ARP TABLE F7: CONFIG",13,13 -.byte 0 +.else +.byte "F1: TFTP BOOT F3: BASIC",13 +.byte "F5: ARP TABLE F7: CONFIG",13,13 +.endif +.byte 0 config_menu_msg: .byte 13,"CONFIGURATION",13,13 @@ -751,20 +849,26 @@ ping_header: .byte "ping",13,0 gopher_header: .byte "gopher",13,0 telnet_header: .byte "telnet",13,0 +file_read_error: .asciiz "ERROR READING FILE" +autoexec_filename: .byte "AUTOEXEC.PRG",0 + .endif -downloading_msg: .asciiz "DOWNLOADING " +downloading_msg: .byte "DOWN" +loading_msg: .asciiz "LOADING " getting_dir_listing_msg: .byte "FETCHING DIRECTORY",13,0 -tftp_dir_listing_fail_msg: - .byte "DIR LISTING FAILED",13,0 +dir_listing_fail_msg: + .byte "DIR FAILED",13,0 tftp_download_fail_msg: .byte "DOWNLOAD FAILED", 13, 0 tftp_download_ok_msg: - .byte "DOWNLOAD OK", 13, 0 - + .byte "DOWN" +load_ok_msg: + .byte "LOAD OK", 13, 0 + current: .byte "CURRENT ",0 @@ -777,9 +881,8 @@ tftp_dir_filemask: tftp_file: .asciiz "BOOTC64.PRG" -no_files_on_server: - .byte "NO MATCHING FILES",13,0 - +no_files: + .byte "NO FILES",13,0 resolving: .byte "RESOLVING ",0 diff --git a/client/tcp_test.bat b/client/tcp_test.bat index 914ba1c..02ac029 100644 --- a/client/tcp_test.bat +++ b/client/tcp_test.bat @@ -1 +1 @@ -start c:\temp\WinVICE-2.1\x64.exe -cart16 nb65\nb65_tcp_cart.bin test\test_cart_api.prg \ No newline at end of file +start c:\temp\WinVICE-2.1\x64.exe -cart16 nb65\nb65_tcp_cart.bin \ No newline at end of file diff --git a/client/test/Makefile b/client/test/Makefile index 6702896..1a5e14d 100644 --- a/client/test/Makefile +++ b/client/test/Makefile @@ -67,8 +67,10 @@ ip65test.dsk: testdns.pg2 testdottedquad.pg2 testtftp.pg2 ripxplore.rb ip65test.dsk -a testdns.pg2 -t AppleBinary test_disk_io.d64: test_disk_io.prg - ripxplore.rb --init CbmDos test_disk_io.d64 -a test_disk_io.prg - + ripxplore.rb --init CbmDos test_disk_io.d64 -a test_disk_io.prg + cp screen_prg.bin screen.prg + ripxplore.rb test_disk_io.d64 -a screen.prg + clean: rm -f *.o *.pg2 *.prg rm -f ip65test.dsk diff --git a/client/test/test_disk_io.s b/client/test/test_disk_io.s index b5d84de..eb699ef 100644 --- a/client/test/test_disk_io.s +++ b/client/test/test_disk_io.s @@ -13,7 +13,10 @@ .import io_track_no .import io_read_sector .import io_read_file_with_callback +.import io_read_file .import io_filename +.import io_filesize +.import io_load_address .import io_callback .import get_key .import ip65_error @@ -54,15 +57,15 @@ basicstub: init: + ;switch to lower case charset + lda #23 + sta $d018 + ;test we can read the catalogue ldax #read_catalogue jsr print -; ldax #dummy_catalogue -; jsr print_catalogue -; rts - lda #01 sta io_device_no @@ -75,8 +78,73 @@ init: @no_error_on_catalogue: ldax #directory_buffer jsr print_catalogue + + ;test we can read without callbacks to fixed buffer + ldax #loading + jsr print + ldax #fname + stax io_filename + jsr print + + + jsr print_cr + lda #01 + sta io_device_no + + ldax #readfile_callback + stax io_callback + ldax #$3000 + jsr io_read_file + bcc :+ + jsr print_error_code rts +: + + ldax io_filesize + jsr print_integer + ldax #bytes_to + jsr print + lda io_load_address+1 + jsr print_hex + lda io_load_address + jsr print_hex + jsr print_cr + + +;test we can read without callbacks to address in file + ldax #loading + jsr print + ldax #fname2 + stax io_filename + jsr print + + + jsr print_cr + lda #01 + sta io_device_no + + ldax #readfile_callback + stax io_callback + ldax #$0000 + jsr io_read_file + bcc :+ + jsr print_error_code + rts +: + + ldax io_filesize + jsr print_integer + ldax #bytes_to + jsr print + lda io_load_address+1 + jsr print_hex + lda io_load_address + jsr print_hex + jsr print_cr + + jsr wait_for_keypress + ;test we can read via callbacks ldax #loading jsr print @@ -93,53 +161,15 @@ init: ldax #sector_buffer jsr io_read_file_with_callback - bcc @no_error_on_file_read + bcc :+ jsr print_error_code -@no_error_on_file_read: +: + + rts - - lda #01 - sta io_track_no - lda #01 - sta io_sector_no - lda #01 - sta io_device_no - ldax #sector_buffer - jsr io_read_sector - bcs @error - - ; jsr dump_sector ;DEBUG - - lda #$12 - sta io_track_no - lda #01 - sta io_sector_no - lda #01 - sta io_device_no - ldax #sector_buffer - jsr io_read_sector - - bcs @error -; jsr dump_sector ;DEBUG - - - - lda #$12 - sta io_track_no - lda #01 - sta io_sector_no - lda #01 - sta io_device_no - ldax #sector_buffer - jsr io_read_sector - - bcs @error - jsr dump_sector ;DEBUG - - rts - + @error: jsr print_cr lda ip65_error @@ -160,7 +190,7 @@ print_catalogue: beq @end_of_filename jmp @print_one_char @end_of_filename: - jsr print_cr + jsr print_cr ldax #filetype jsr print jsr read_byte_from_buffer @@ -286,6 +316,9 @@ read_catalogue: fname: .byte "TEST_DISK_IO.PRG",0 +fname2: + .byte "SCREEN.PRG",0 + loading: .byte "LOADING ",0 .rodata @@ -304,9 +337,8 @@ ok: .byte "OK ", 0 bytes: - .byte "BYTES.",13, 0 + .byte " BYTES.", 0 + +bytes_to: + .byte " BYTES TO $", 0 -dummy_catalogue: -.byte"FILE 1",0,$81,$34,$12 -.byte "FILE 2",0,$82,$f0,$0d -.byte 0 diff --git a/client/test/test_parser.s b/client/test/test_parser.s index d7bbf34..e697f43 100644 --- a/client/test/test_parser.s +++ b/client/test/test_parser.s @@ -49,6 +49,9 @@ init: ldax #url_1 jsr test_url_parse + jsr wait_key + + ldax #url_2 jsr test_url_parse ldax #url_3 @@ -137,10 +140,8 @@ print_parsed_url: jsr print_dotted_quad ldax #port jsr print - lda url_port+1 - jsr print_hex - lda url_port - jsr print_hex + ldax url_port + jsr print_integer ldax #type jsr print lda url_resource_type @@ -165,7 +166,7 @@ title: .byte "<title>",0 url_1: -.byte "http://www.jamtronix.com/",0 +.byte "http://www.jamtronix.com/",13,"not part of URL!",0 url_2: .byte "http://www.jamtronix.com/goober",0 @@ -203,7 +204,7 @@ url_c: parsing: .asciiz "PARSING " ip: .asciiz "IP: " -port: .asciiz " PORT: $" +port: .asciiz " PORT: " type: .asciiz " TYPE:" selector: .asciiz "SELECTOR: " press_a_key: .byte "PRESS ANY KEY TO CONTINUE",13,0 diff --git a/dist/make_dist.rb b/dist/make_dist.rb index 4dbeaa9..57f6484 100644 --- a/dist/make_dist.rb +++ b/dist/make_dist.rb @@ -41,7 +41,7 @@ end ["client/inc/common.i","inc"], ["client/inc/nb65_constants.i","inc"], ["client/examples/dasm_example.asm","examples/"], - +["client/examples/upnatom.d64","c64/"], #["client/nb65/d64_upload.s","examples/"], #["client/nb65/nb65_skeleton.s","examples/"], ].each do |args| diff --git a/dist/version_number.txt b/dist/version_number.txt index f5b38be..f1bf126 100644 --- a/dist/version_number.txt +++ b/dist/version_number.txt @@ -1 +1 @@ -0.9.25 \ No newline at end of file +0.9.30 \ No newline at end of file diff --git a/doc/README.C64.html b/doc/README.C64.html index bf5c18a..a77fbce 100644 --- a/doc/README.C64.html +++ b/doc/README.C64.html @@ -28,7 +28,8 @@ When the cartridge starts, it will attempt to configure the IP stack via DHCP. I then the IP stack will fall back to using the IP configuration built into the cartridge. See the section "IP CONFIGURATION" for info on how to modify the cartridge defaults prior to burning an image. <p> -Once the IP stack is initialised, the "main menu" screen will be displayed. There are slight variations between the menus shown on the 2 "flavours". +Once the IP stack is initialised, an attempt to boot from disk will take place (on the 16KB image only - see <a href="#autoexec">autoexec</a> section for more). If this fails, or the 8KB cartridge is being used, +the "main menu" screen will be displayed. There are slight variations between the menus shown on the 2 "flavours". <h3>Main Menu - UDP only carts</h3> <ul> <li>F1 : TFTP BOOT. This will query a TFTP server for a list of PRG files, and allow the selection of a file to be downloaded and executed. Most 'single load' applications should work</li> @@ -41,7 +42,11 @@ Once the IP stack is initialised, the "main menu" screen will be displayed. Ther <ul> <li>F1 : TFTP BOOT. This will query a TFTP server for a list of PRG files, and allow the selection of a file to be downloaded and executed. This version of TFTP BOOT is restricted to running only pure machine language programs, since BASIC is not available when this cart is active. (NB - programs that are pure M/L except for a BASIC stub consisting of a single SYS call will be executed). - be loaded </li> + </li> + <li>F2 : DISK BOOT. This will display a catalogue of files in drive #8 and allow the selection of a file to be executed. This version of DISK BOOT is restricted to running only + pure machine language programs, since BASIC is not available when this cart is active. (NB - programs that are pure M/L except for a BASIC stub consisting of a single SYS call will be executed). +</li> + <li>F3 : NET APPS. This will bring up another menu of network-orientated applications (see below for more details). <li>F5 : ARP TABLE. This will show a table of the current mapping of IP and MAC addresses </li> <li>F7 : CONFIG. This brings up a menu where the IP configuration can be modified. Changes made here will be persistent until the next reboot.</li> @@ -78,6 +83,10 @@ starting with a highlighted letter (e.g. the first link on a page will have an i the 2nd link on a page will have an inverse "B" next to it etc). Press the letter assigned to the link to load up that resource. +<a name="autoexec"><h2>AUTOEXEC</h2></a> +Once the 16KB cartridge image has initialised the IP stack, it will look for a file called "autoexec.prg" on the default drive. If this file is found it will be loaded (using the load address specified by the first 2 +bytes of the file) and executed. NB - since BASIC is not available only 100% Machine Language programs can be executed, although if there is a BASIC stub consisting of a single line that does a SYS +call, the target of the SYS call will be used as the address to start execution from. <h2>IP CONFIGURATION</h2> There is a script in the "bin" folder called "set_ip_config.rb" that can be used to modify the MAC address and IP config details in the cart image before diff --git a/doc/nb65_api_technical_reference.doc b/doc/nb65_api_technical_reference.doc index 8738964296a9b3bc086fb141d274fdde9e8d5adf..1a2fd29646a1159bc3749b79c9739661fd251aa5 100644 GIT binary patch delta 34521 zcmc)T2VB)w|M>AFAfN&+9H8O_t*fGdfTJ#4b=7U%6HurFw{>2ryH%@3O^3B=)jH}# z#ag#kt+tNty7zdht)s2hTK#{%$qihn)Td8hzt<mp-<xrAl6=oea!zh8j=z~}{Qlgt z!ppv*D%MY4Md{D5j6Qzy<cUn|!$=~MkQT9CTcOU;ifI$(>cLuQ!7ArfW!e(O)PA~^ z$MbqCt+3uti*%mOX-$#$5{0edzk3g`l9;co-CTWT*K+RfRY}p8>Gj#9bww2AriY@~ z^JjKc6o;lL?-o;>%2`iE85yD|g;?NC-sLCj`zxV}@{kPTODjqRGMrULQQjh*9~qI4 zl81Ns`ETA!k#22gMOngvExY9=Qzld<gM!4QKrVaV|E$1hA*ZZae(d)@D_G<9yDaGb zV=r#M+k0ZCk?~WEG-5f~<G3o;oUHGX|07kYoL(k&Mp-kK5V-%uRna5#m@s?${yIBj zeUBMz)m%}wvP4to*p}8DdqsBPF1j52#>XWO6LssyF2vs}D#~T1=4_!TlyCaa?;R9H zDoHmsESz^KZ21WxT^G`C>Sm3fVdg88sf;t|p^R<Gb~J1*+i{2_b~-C{M&)eKih16O zQnsw3tRlZJ%dtG~b_BKO73CKe`i*yQl`KO;C8_*meYXp*az~Pt_Ifc&QHpuU64kS& zw8!n8vBUe<cfFYR!bp4D?Zuw`&RT0_yPa}u=JZvRD+N=V`F!fB&+-i}&^xJLf}>w@ z&v?h+<Y7aT1|)cU>-qhvc~uRs5>vT~-pVi19z5+AVhvW+*ZY;W2bmgfrmYt0Py^~E z4;Y#-VCayX1spA7Uu@92K|@FPw#jjkm8)8599<GgrH?OA)LcF)TyO3l>MQf2!d>%X z16=bW^!NNrTFD~xig~P+M@8zl{9mx9M#|J&=E9X_<$C|n!ur5G`K`H?GnR?c*X40o zQ=>AbM(dCAl(VKr>tpk}@{iG5=Y7ta8Y5H7n)z3e{2L4&oIE(2U5l!ckwsPgR^C!p z7FG4*`CJ)S(`)4mx29H0pBf#mznia;pR6O=6@V&wN`auf=9CCoYukoxTRIx*YW^TA zS%ej*YLd*%E;>?glt0WKJdr=Npt-Oal7>>cv2k$;Lx$*2@)ffZRn|uq2n{e3Maddk z4o(_C$@j6=Vx@`F-wz122BY=g^IH*#j?S1Gqc<y1#!3*QcPkibR)T0VPR;AaL_69H z9Xu><=&->F*#s<FKU>Hu-RP?N;sRD=7(7?Vwc2X>lLFD!3aZIev!G+b&4M2AN{F7P zkX6tz;WF3U=NL0!?)yBbWk5zkYEfZVHj!pF>-rbYm`iOc9GXL-5~GI{tYWS?hDu~F zVunf|GcYt`ktlsk`XWCTjIkDpcAMC|P-Sakj7+o!tLWZ^L;TJ4RI!ROBBLy0s_HKn zc35+(T5}`A(@Q$0n!dDfS+g3%RCCQuKbES5>kkWunTu9&Ri((vsnJC?1gXCI`}qP> zpF|$^&+F%F)u&+t;)W(A4;YgAeQatT)i?ExRxfxsV+V|M%#Py_9-Q3I5trC=aL>4* z34`Z2$|hAzsOYFsDl)u+BWXZfzhUuGTk5rTlr0rrzgz`JvxW@BmUAQzb~JC-Os0mH zbBsvp*U!-_!7+4jLeHTI@s6HD977Wm90>#B9m&1T!FH{iRrK>~o0v4jk<@=+zl7AU z+m7|D>{fh^-u7ntm5K`YD;wLgv152-Id7?8-5WM;)}VW{7P0mHn(15XI-K!IL;8|k z#=O{i^%}Hk)4hI!mmAk>;24saJgi^5Y|xN|p|UZ(ll%2c9x<f4A6x6F=@^m7Rt`@X z?1)boo)nkh7(tSLJ%^CgJbKuHvh1Gh!R+{i-aUu)8#>1kKR9W4f+I=xCdtW8SbNwz zX>j^t<;XpP+^uO=`s4wQAtQ$jP3Z56t2MP`RR=SAI_kwl$wHAVG$etth<B|eo29DR zOZl)^gQ0O1{ZebxE3D?OPc-ZUe^ATn`oj9p=Q1M}D?!n}sGm|GW3+qA7LA*?ZP41G zcWMy&l36B%DQQ%KT~cy=AOhyK!Y<b#J%=+XX@KJ|5toeHH15>E+uP4KwN=OQ{&}*T z5mJx$Ih(`R*R7=W*~!6q-EuO^RX^XqvhGas)+75C%VBP<-oEeG`l5ajPIF8Wd3%=% zk3ocYeXHCC4C~)3VX#%I^Rtwot7xSlU1gXN+`pFE{On7u1&2d6#Ww$+6`NIRsUP(} zu5lEO8Q9!QBWQ<*kMd1zFrj(w@*aMEo_>DTt94CvI2>KdJ6>wg`o#tfTeN;Dwyh%| zvY?-zU+V0c1693_o>y0Ay%AWK25D0?Lmc9ffZqDDS<e-FXa3CjGe`HgS~I*BEtr@z zrlV3Qaqf#N^z*X<b7qXq`Y=E*G`onGSE0n(zOkv*XCKY)SB#9xAr=GlN^=8^ignbt z^i50hd*shsCH2(OQtI=bGSl9QeGC_(;n5T8q4?3yz+U9>*DGBtrp6g93#oy6hl^#5 zE``)G`qHJ9yl2t$j}P|JH{6ZVi_E)LH&<`+c?}aV5tFbOOYi}9U?+BAH}+sJWc~Z` zsoryWfv{f~`W3$+FI$rj`B4A?C<xiw!YHDrtO$##=c6cIK59On6f;ChJ0G=wBgJz7 zhWV)FHJ=oZR}RpYB#nC{iJe!N71YPeE9PlzrLc3+U-+<SQ39SNw}ABdgFY;jsUU*# zfA00PbvWINPYG|5S8?08eAX7bO;H?0*s&-?BL-Da8+8zi&gg=!=!Wj-0V%#-h=UX3 zk>ac9&J{VrW-|OHW?>ap<0GuWTC9T<{RV6_gIXomvxB;g7VF;!wWgIKJ!t<tsJpGg z%~EjxIH()hv7<PK<2Zq{IEV9ih(GWMf8sHoK#m0!8iG**4nHMQ31OfiUPL4GLLB0e zfZpf>Igpa@ik`A6EarK#d{$LmOU3{Ds76>R{zX(bS*81LqxuRvHW^be71OW?i?IaT zu>(7?3%juga%S0w{Wy;cxF}J*#K0~5grAYqUr}-)H+<m-f8;@4<kM5uhsBg&=vh&f z&H4MNK4+!)7g1edmF~ZdYCd)+3>8rc;i!lDXn^kMfu87vIK(3Xz0n5~FcFjdiRxqq z=3zbzY`{jOViPuF3(~L^ADdAvkoVb9ouB7fQ4O?G{EMhAV2l1*a{u@YAXP>pBs;0F zQLV5Rx!BFl*n&LyNQ15L%TJWC6Cnkt=eU9T-1b-zpp;Pj3o6PWoPdM7JOi*Fa_=Uj zupY6loc``QKYi!gMA!RkyeBw6X8qsbTYQJ_aTe!r9uM&c9^p?s#uLaktI!aP5^x{{ zl@N{yG(|JKgyv|0mXQ5zorfD~ZS<6lVKLt9*0YY8*9pU4Bkz9E^s`d@i_)EC)v|wk z%(P+ek}(j2Fc{M@12eG#E3pcz@e$TQYQQ?I$LIJ0NAqyZ9An@*Zs0%gWIJ=nw!#}e z$O)-Fx#6p)Yzd3`g9`GjsE)JJWZ7f)sH&NB`5&4h6RpzCvW|az%=ofnWl;|0@jR-b zI$l6Kyo~nffR5+{Ii9+pD@I}zM&~7}V;Ime7xS<V>#+f;*o4iHgE0+T&8Ys&5%#R8 z4z|){*<<&p{>p*tmdpPT)mN<?vaI7DM|CSZ_BFo2xA+b>aSK1ehl467a={mJkoiN- zPI-|JVW@~o`G{&b19edk_0bL8(F45@hj_@TtvCAUDO<y0E^(f6i$|7*uGMVrZKcVw z$L>+R!5Pmjm;WKE{j40atRv!|Hk<qC5!(v%pU3Xahk*^)h*WIG7NkMW?jK_tzQuR= z9zWne_z^eZ#X%&eNgw1wZumm34F1TYr)&?4`7SV10G=AgPF9*MyXYRqGu&MNcVX;d z<&b3^|G0AIVaJ|F1%#m@YN8fuqa!+@GrFQ1x<f9NJ<$u};Kca+RL&Fz-o*mEhcs-( z$M^)>u>*3Q-G$v|RF4$R6pyDywYilh%O1N&^+ZsnT>gj3+1AP-%R2sXRClvur*Q^n zaSo611PW*SAQVM01fv8TkcLMnN}@LEAhrNet;;|h;*kI+#v=t2FcFg=t(Ga6s;BG> zi`i8?Q#_s;)w))iEPL!8)lZ9M%H@BEY9lL$EbI8kQJu<;EyXe{$A>t8gE)kXxP;5N zifgzIX~z8rKf;@Hx({*&5Y=1^gdh|pQ4_UL8?mU1dXP3?12oiAc8A5JmdF&3r$#l# zN|R-e-J|+(aHd@Thp5)Da>%lde;n0@>{u_vAsz{sgvpqK#aMz5unf!bA!rmUE3pa( za1e(is-H3NBW~gryt!2SASZIe7k-cibspr^Q}&0&d>E1`9#4&Gc`HqpJ$8@k8b_vF z{)ecBTRCJ|$3Kp0UUsYk!cY;F&>St$60e{y`e6W)F%Z(jkhaYbyop(uU682GVPGxR zVLcAx2tLPA9K&%)_vA}_Wk&Vgl9}T1)TjnqX|n9GdsG+mdHKJ)T9vYL$g+-q9M!Mb zv7d1pcW@Vf;xV2ekhVn-ilR7zQ3BFm3qdGqp*HFiBC4?rG(|JKgg!__68b_KEBzsz zy<`m3M;-{NyX!iqaiyerr}53_GR5Yp5zcR=$+GwE5uV2b4F4{|MXVgMtm7X?cpy7D z9WyW!Z(=c)-~;TyPVB-S?8QDvPxw<Dz(rib<wDBy$`uA4;ZHn95t^8R2tqLwM=(kh z)<+$PikZMeAZ~$pYB;^DG+B1lJ)F~cq{J<k|KT*?Yvqt-9se|(!E$rJfe?hE669Hn zDyWB^kVbbOOvEhA#Zp|x6<mcEt-Y~Dc(7&?19RY{i8%#puo)$2xFn%JMq(63qacl@ zDu_i}9K<o4KzlCB3-AH<Vjr&HCVoSGng(0(IljQr;@q4)#lY3#sox$duR7iL%HC3q zjq8XXP>~I@H$*bukE1Mq8X8xx$(Vw2TrKxvAI@<8J&U%S>-(WU9?Icn|NLqXD5=iF zDXQ}-nf>m{(1*<Ykq<4<60Ok=FQX$mp)1y4E55~T+`&EkhWmJkKkyhqY*-1DL0QNK zS3~s@6v-$CMq>=dV-fbi-lP2tAHogX#{;MiKG8sR)Ilq>MjNz82Xw@A%)t9Ng+~v5 zx&6!S8?N6+S8m@(ef+tmI%VEzJS}=tiQq?Ai&SjKUi9ajJRBo15eDAEN_>oM*oD)$ zj4QZ`9CE$Db101_CAn8Mlz~xr1Jg^U&OAC;?IcB4l?AFnRwfJ2VE#;eiFTZ_ZsI-) zaZDG+^Qc{x(nBgXp$!iMwT0t(4kVO)KDE>F<*IW!dD!`V#PA7MzCXI<`y8j<K1kwt zma{@Z^0(Jji@e8T9F~#q$C7siZgpS@KEP6}L@IKG@zE3tAS{e_d_@MD;w3aki?Gx= zUk*@9mn9?j3}j(hNYY0m7R|%-PREPs!wv_fcKLd#TH0Ekon>AeHljpDb{hAfRHABE z(t|G-(_5Yl(tBJD(g$4j)59?9+jg2#Hg&_}!m5)ew^*<>B%@xC%vN9nB-3CrlZ@}W zGL}ca9^wgPCEmydKXZjN(ReR_(s9V@nhgge^*oTu1c)5(LxQ-k64<2GjI7dIe z#wlFHb=<&z@DsEMZWu+R?)|=&THK#y+*d9ul@(q=f1`F)wY1T+Dj!bP)SDTLf>nS0 z@X12O=E$d%t0~YzrPRWu)W*G4_DY-IPw|LS^$kBgDc3tn9XWKxuve+Wl96m^)(w=6 zyAe*PTM}kJeeG3$KFO_Ytz-Me{QBwBIkhh3%y<`5yt8ipTy|kGjzmad89ltzC^j+P zTKx3coSJv})D3r~sJwKNN%D7Gi>%=NFpl7Je1W65jvM$9H=#um39P_coX0iX!cVx3 zySNAc%0vW%Q9Fuifp_q(lb6Lfj&JZA{zT<yK958Us-ha2V=%T<<Me@>xQE~I5P#qa ze5$iT6hR`Akc{>C{mw1<y>aEjnIF!aICHqd30_Z_!w1gnI<xh}nN8^vKC<SmSoH3q z6>}G@piYEeSmqHC`J;aPLZ#Bx-Qy#%v120fkXT3z`Wxj6sg=vNrTEi-{$_+n^~%~M zT*|6N8_PeV77h0EVV7lPKf@08DFXd2ag=yQMN<b3YH`n-C0<GKmcT~CJ*Yz%zgLCl z8zBLf;7U-<Iv!{wP{)my{nViJ7eBqL=3{THtIGRxipi$RKWA2Xb7OP47nT%~6p$3o z&gyJvIrEIt^7=e=SL*t^MWlwxyfo}E(@-mUKaDfEjr;I?fzu*lP!-E-P@(V}YSg6G zA*vQ<M0|jyScWw?gR{7TYxog2aSIQfy!hAVBmgJg#$J36uR4StIgtky5P=$~hsJ1v zrs#nC_wL{O@#@91C;9#M?3ZW1JbCo+$$dLd-rI6=<Cc4CS1w(<XaT?TW=xwjZN{{v zYf}`~0e<v=TR%qAQ%y|p?8j3X-#33=iS|iRB6hOxyRZ)u7l}zV_P;usvF|U{kQ0D; zww5z>%j-e<tlxq-mE^O^ZtH)-v{%B+n&D7<USbJ}R2|q6lE_%m(ChqG%&3)D4bq$5 z=EVO~C9PmZmV8dt_y1fYRHAhU4<W&lpwxgJfEIO(I=R$xl7XMz;dYSPSdVxRIPbXX zW!3cEocgn=JU(XSInpOeKX@-h|MpJ#&=zFe0fR9QORydrun~Lf$j0im?#8N3<$?4d zPY<O}JjX6Ig=?`n^roDp2UTJZTBuIRY!D>FxsXf^yo)7RiFL47vWMZlIDq3giBpJ< zr4FJ7hGQ(IVFAk2rBhgs<^#GR0c)_c9u2t2`qUZp#V}04R7`l0h7n%JK^#TRMw|`t z8NR|voWknHoEhQKgf<2o2tgUdqc7Is{@tIh-97h{{GR&mt0M=Fd{utek+0-8O<vZ2 zwbGop#2S(L@A5lWMyZL_f1MV&qst1aW}!qgqUp8rM&C(ND}4(l%1|NmRZ_{A{`<|M zrIhM>iO~U9tRzknqluUVD^9M0^P}*B^rWSJ{_If3Ph(?du<CG>Ex%luBjs0&%jDnI z8csR-5BHy|o6}5ZU5fj}Cnv^p=t`lKrcmsHkwQs?ltpj!fs}`oL;eO*eTx|_a;bh# zeW4lPr`LQ?h8msIs?lF$zin;OTL782OZJO<{tamDAP(`^h2600g4BZ(m`<6mz&`A6 zl)imstb?@XgQ$!RWvi}PNBp;clq>DGpCvwp)F7!jeF<_u9D>vssV!Mf{eDsysuh2I z`J?LAK91wcmreY`?4y=QOMU~e22umpV*@thIC?a-iv6)!<VD$vjGxRSR{3i+A6VsI zDKgvjTW5_<V>jw+E{5pWpA>Q0YnHX{!ZFv{pKrmr8<9xHa2&vQ7}S!J0)}8HhG8_O zV+LknHkRPER`m8-(~&}ZypGwph)b<G$S*T+2i|Rn3D%<&4T9%T4sBniLxRqjfw?$~ z-;l38l@JbaA^GY4y=!tkIgiude|`Mx&kyZB^vR~(yzV}<e#Hmt-(Uak`nl8BPhUR? zW;A^>V#>*_qK8_+pM#SCnTK+nfrE?=9%{1Fyht;nMD&MppEU99(YrX7wM423V_Q7- z+G9DbsT}d7Hs`0G31rN?KJ{qI{RMp6jBq0FOPmu?w7C^>3HL}8AbeJ+)2p%TFzfH5 zmf8HKI!KLhF9r#{9rjSdo&pKKgkHi<xRrrModrBVSnFbtG0szsm6sr6m#11<J0Hne zzkpg<s*#dg(n`r(hm?+#OkLE=QbHXlp^P?vkTm==UO4vkAoB<_&gWD;t)t_O%GuV= zs%{M;$#^`@;CJk7NBu-Diaj@^N=S8(;=ik>%n8(I%<&Tj!HF_@_^=%{SE(O!S?`;^ z365Y%UI2w5wWbnsc3{7-s)OAzD5JOdHYij7!nmGWEo7YcQgdiEqFg;XZ^@vdE0dkP z`;dubmdBN06W&*16;|UT9K+W*gR{udk(!3W@a{y59cpK8!NGwLltB&D!xT)%d?zmp zk-rNIBLwA89?zp0#$X=i!@yg38y{goS3U;>7ry^oI`z}3pT0VF<lwGtyAEzyxB3HK z`Q3GJ!IlLF=WUs{W%a?;>sB9}`TFE>lkEe>*zKnlaZ<~Rw>f4W1)YM+aulp6vBK>L zsW+5*oPNBR^U{w88H+KGlf>{HW-56vlxU8TuemJ8lceLg$+Vnxe61+q$;!NpbAD<G z<F=+&Y)tZv0d-x;Whi}I33o4ry+r;1q%_=1p(>>?5B-fwP1Hahx9wU>4K$K!aed47 zWv!R$W!A7L9_ytoC9NuRPd%koB~h6pSLJI_+1C4-ZuMT)ei#b%AP2l)*9)lw4P8av zoFZ(2`CZ%^2Q&~KKmAIa`4YYPxay(mJv47~yK73sbDCRcw*|~S@pZ2@Qe~vNEJiAR z#7(3}s%2@;D_xfQ8M8{Oe*P4D#!m}6alzI>csY++w2~Pjv$>QxL~qgI%I3o9%m|Us z(>CKSoAMjHyKz$x)e(zEcnMuF3kKfCX`I1j{0BecHoUuY&crmlf$5k5=S*HU;S=n^ zUL3?}wCTZR3Z2jsi5P%n48%}O#uV(wr#OP2@GJhnBm4>fo?Hk}8Wj+R4Ly~*%0>nr z{qpG6FSj1udc?~w{9ZL*ts#5(oQ(eR=+sw7PaWKKN`5~{TeoUS+PlWCK(&q$?Wew8 zqNbFbgh;~E7<PzeGkm6-b-9too7!3Lx->9zbF+@ItBG3DIGIl^Wo*x{=C@AkjbpMc zySirC$!Q{UC9C1Vg~eM+L85;Xb_p~ls1qOomq7m}ca;LIjVYVC?W4H;WS(QTij3>| zIB#cc=ELka(`p;Bna1K;YCy1$tau;nQkD{y(v?!R+JR4t4m~(M%B<@if3%G<jQna9 zD?qC`H)K<}%cciN@p7*-QeUL5NIiL!zE-*MAWgy`uCCRsqe`x>#!)ZT-`JX8b=`RA zQZ?K4MY*j{^8FK!A$2c5r2a|0lPb3byO4gI|NR+}lcBeKy~5hALxt2L*53P7%XVw5 zJAdX?B&V!tH}>NTT)>@PY#qkOS>5)tL0p~98?45FAa#Vcvzpm(ao%k<o9ZOnybpE` z=Uut{#JfQ*w;?&+$M5(Pl6#Ig7Dg*LF&-(HiKSSEjrblvAWuBUIg*f!K~7$VV=QF& zHLSpQ_#QvtJRZP1fyM#~qAf;Z6vksc(!eJQH!nXp|LBLyUmksc!(To)u;=3gn-6S$ zwD#k*2l%)7z}f?=mM?sJ`Mgy#C%@*Keq~nJ(=S7~t>6);PV(jID+gMk#F*w+jLCs& z^>SQW8CD|fU7W=3$ZpCm%I?Ts$QEa`>o^bCM_`2hqE)tsi`g|z?p0+|v#tJ)*?RW2 zgYY9+{|pI)1VMIOc6w)m5gM%S(}G@bD^DK@#mCrTrl2nJ{y9$L44U`mI*4}Ygs~Wh zNtl8+Fdgq;9o8d%BIi~#Msr-kWn96J2uR`>aPm?dE3pczu@>vF5!X@a74B@H3Z~%U zy_?rBpZ@Nv&-Y*c>hk_wTQ^)@xpdKjxAX;<r%(K9|E{k#T>fhOm{-RP9oWz8Ufk>) zXuiMF{7duwoWJ$Hu+bWd@T5b%RL?59hzKT7kPj*ZP>}Mf(9)52=}?s4NgO0D`yjEA zxJXPS4%2(vjoMhw7j3MvI~}U#)IPQ1TvqNxDBcpI8JG=;mBh)8iNs@7lH1W|eZ=8@ z)6CdfN)6Kn)-a2%l9{EC9W{wqU|(9NsE?lLg(M7zb!ssxHsN{`r?>nz&}jQIt@pvf zYH?$IX|-%cjdoOc>te3^R8uORv|3a}Yi3*T+?oqHrC_Gw3VuQ%R#_NDAe&Vj!H~_X z2ie4?Xoi>296exfb|S;F>HRSbvJ2xd9uqJT&MCZ1g^qbxh$HwGDy7dIOC<+#BL-Da z8^hqlJ{0IrRYXAq!GUNrLR)Z#cyRjxHSYZBn_nM0bo%RKU+><!d;QkkTThqy=(PME z;&%m>zGwcvJ@4%qQ#m_OcV2CALF$f?znr?VfEioP3cX6qGV5W<R)NNra_V!zQZb`t z|D2HhlYNujnv37$J8iDJJA<unp?zLc=UZq-@uuojebKyb`pw_{i>@4fja`)mBo1%m zJ$w$=m*UE+o3v52+!lPVXFleBffM)!YCoe=uo^=J9wRlPKrvlf)MaaDSN@^)CUkIQ zv>QDo?i#$13lev08*2Baf#0&O+^o_E${O+;hr`t(+UeSEn>DXTLA#`0U`Z*h;psV4 zl)ZSpj(hT+0n8nN(Xh+ZZJ(>gx~J|@P>Os%4&if1(YqzTnmKu>T{EOcv~kslcD#2% z5(f8|Zwwl>s;W!1xpmW5Wh|<u_Hat|k;FqF-*B9ZPjLW8@HNihIy?tZe(*wG1R?~X zD2Y<2h;T$98ZoGYrf7kdXoc1oIDor8Ll_u|377^Q^Y9kl!>721iOI}|j{86Vc=^Zk z+}Jt&`N6&04sP4Lcg?cR%Qn9!FK^A7I`Q;aekYzDIrj9(!PIg&VJ>YM#0gVg<({kg z&eV<{{!(343o8`{vwISP5s)zKhb!bYD9^g}+`?8<?NM6h+hLN>xbId+wzfBt$w1=X zh*MaXTIyu&wtFtM)gA?tNktHpp?DQ@;aY1Q*1E8M`dT@NdIZXB3C-J(&`3xo;ddin zC$)-H+c{dZ25!r|-@_^4T7YGe4iE7sVg~XV0S4ezjKVlLF&-(Hh&?!lGq`|j@EAlr zfET<`1~GU6wGoTD=rM@$O=4gOMq&cyV-Yss9NwjbR$?{!4&g>C24WC~Vi?9@8kXP# zJo@#Kd`x-c?2WTmZd^I(`n~bZiO;_I?3+CwpV-LnTC6&eYt`~qi|@QM|IW<Ce9{qM z%zQyDVk~H&7VX(IEQeVY4*76ek(&&SMwk_)qt^o}ieI<yXxZ5xD9M<0<Do)x)iu`| z&q&QUxtPmwR^{DT-$1Qi>8VvO$}Wk=l!Qh^?TF_v@^zDvY)DBy%v_S8cElyxbzn!i zF~cvT`yiuEX*I^^La#f$`z?KNNBRd^(bjzlxf@|_sM-BChjclOu4wq|(`d1b)2L5( z)=C*G@kqo(+4}N&#kqdQu5D^=Ry)FMW%!z_oh7U2@}gVyoY$$SwLueEcoL>z1_n|W z(vO3;f~9D3nwysCu7sIku11+MEMjFS84SWO$SGje;Ec<LJctltCNp<>YollHbcKwa z_FP4FQdid@6?>sjC!-<tH`&Pdiduyr1R3rZlL}@ef~B@)w=4Y`XWEsr<~I5ylc#+= z$T1-Ge<@aDEA|Yr&oWE3peC7ea9TOoOY9}}5#%4rA&d5SaTrC1L70pwn2P0Ci$ge! zBRGcd@dGa4h2ebIhFYkNx@d@A=!;2CUZ!FJ-oq!@g}vB^i@1!dxCVtI&>O{Ip5i&l ze}DYQzE94lrSf||ZC%=`<?EKLduPs!Ig`e&8@X=qNb}cqif`>)$FV7|ay;{HyxvA_ zTZG}!qsy9Y9XVR9<FrJ(FN}xv)h>mZJH5qPw<BNDa3`^A1GNm-g+0Ve;&uRsQGl2Y zGH(e=EA?aTP<q5K6_j}aD1u^WNQ~^X%}6__sa-mkf@Pi^dpqWDGHn5t4YRM;LDIu- zWgfn_$EgL3W6f#t%$DOQsM+7v+EZ?asrNGSl=55!Dbw$bd_&dE#^){RwwalI_KeKj zn@)BGI73EK32IPo_D)JbUVSNjCo9T4yMjoikUFpw8?X%==@~!$^pMMeM$=&0Uo`r4 zEu^`XQ494_K}l>^9;rCRsUYV0fx~Q*cG#8DYHRk$FLQ$tiqc3KAqRgsRsP!4aI4#M zpp#nIYC68!;%@^ft=A52eF#sfo04e+8bYobP0$qa=!@}q4fF6e7Gfn<V+}Ur5Wc}B zT)`~_yh;-v#Sx4Ugu?k8FXa%4rnrPF$TgCF1AO6!FvOrCUPJ==V;Ww^8|XcXi#<kT z$7ntt$D;>#tlvksuBQK9JoTOV`_-xEE`D|K*r8+lcBQTR@cmtH&D}L?*IT=$XB3?^ zWDJf|UzM8-*IxdP<8b+qc}92}wM%i1Fpf4QDuq3is7%J|xD3}xz6*_=3N7u5BQft$ zP-aVDZa{)_nf-Mo??&>IS(Cd$a)b9<kg&KVpV}%@?!j53Cedn+&WImrpHX?BXAF%u zXCpN%{Tga)i&x8PF|FP5d%H)Wc$SlBCqW`^McS-)MnW)m>1x_{OE&dZ%UM-VK2>}A z-IAyFT5?Fq^+8`Gqb4Qi+Qmd7@>ZMlO)?fG(YloRwNMugu^q1TuaLfB+sx^MWWHS^ z9x(g_zGJA8W9;*hpM5?GwGJafk;^#RPYuw1anGzj{R*j8mvA4FSs^kif<Oc#2C-<1 zm(c+o(G7242HwO{tieWX#}1srC6pV>vv3%LaoB*3NF7VNa~A`<aTJZlu@ZDZXLLbd z^an|m7~KE)+Bu&0ICbpQS4Vj4<Ew-FK1p4>eBr)#<agfe8I#7&7&&9u$YCRAbJolB zLj~Vd=5_O;<5T}ntJ!F*7@^k6o8Exz)!qCk$~fLjZQ#Mx$jBR~7W8;7{(Hj_r?xJv zJYSIAkq`wz!qX0JWwoST=CaB!^CfJZAR(JRCQAe7Zi)BkE6Jsz<;%~mX4aio^X{DI zbh4A^&xAx@BEBCdaMD#3q)YRZLu3D7HE)o#F3k(B*}9ZHSIm~0XM4AO=%aQo<b9GH zr7X`u%CkI0<=W|?RM3s?3k;(|%lzk22~zH3;Y$B1>3ei=TY;pP`BE=l!(^<7TYCSF znbQZ!e7g?W)#p>DeK9uE31N=5-F?ZiY7hS}NF^2ROPn2R_`jw;)L!XC_LYokPW7Pw zS*D-ECH&@^uXWCt{~Ocq;}7H>XD$DZ+x*;ARA1ypMc4euE*Z;LWO^l3Mm^X3!*28I zF}*(IKxprp->YlJ^6i-}2ZtOO{ao`Oy3Oy$bU9cCVJh=yv43;AWh_6H>C-R+3tjWe zch8u=km>K^18jHA-|IGiJJWYy5595D@6;n>`EQv1El%RPYyMrg`PZ3#1Gn(RHUIUV z>C5MP!gM*%JrRg;Mu%-`X`|*`^+2)la*%{_<Nqn=Vk6{zHPoo(RJR3YbOAghhadza z+G&;6?q2C@D-_LiIi*xVT{J~or%`BudROZZXQg{jJ?L~#)0Q;t@G?4}GrFKFx?v0^ z;$3V*K{k6dHX;?9u@xU<J9c0<j^ZRP;1X`2Ukas}LOfC#IDn%#hbw6N8V4{g;WB>4 zL$sX0jT?M|eMsP5vD_~%F`1jAXpWXBJB4b9X!OJVdpGV~xxgdESML3A@0)uk?j3gh z9;mSQ-p*}1w{6+Baoff%8@H|9vUcaHALy?yS-9$*`R}Y!GW{3}SE^wiH#)C1&aPBL zjC>>133-*OcGM(70pkt-rRrtl?n1SwcH0Vbt@qWv{sHW=gv)_ZB-*4t(I&*3#bC}K z#dHbfRP1%lFO-lme=pPb;WJz`SMo$#nK3{3BGWHHLQ7bcHE8aNUdsuxQRR&4%Xvh2 zz;g9{twwJv=MUL3JErZ4=NwYLrv6fln%X!1Snq7B<9F^gaSjXWcpI%JTDz9mCu7rF zPvn*$I^zB$YyKZ@^Y2rD50Ha`YcbgszxBj#W}=<RK0oG3!FEN|6jz#!qzOr~)9m$+ zqNJt7>tYOvB*vRE<~yY(^hYuVV+e+01V&*jccUl4XBr(fOvgODjRm*?Pp+}KkQ@0? z56Kvap?DP&umUTw7e{dWb*lOu1_Ixpx}p@iV+3AB3MOL;(r_3r(7vsKh1h}LkYffN zJJiJo*oai@!8fQqlY4BxTEBOGl^ax7Zty7mogdDW{N{}Hd*Tc3P+j=+#)Ul>c5K;k zVdH9kKm1_92XD=O%Xo8_+Anu-!d|l_aUyb<(P*XGqynvr#v^Z=)2RJsN42ZZRT|hb zR&9*L4inI2sulMaU&$z!F2uAeUV-;CYyQcM`4Qg4)sA^FhOfKDx*s94<0#?FMOY>p zwYI4#_AQ>~TK~S*x;|0&c9r=OS_!FyPPzyZE(y^>Lh~3;Q20$gQlUxk5*)MX=HQJv zlsQ6avXsOSti#K5xnSYGv2Hsj>`%7S@L0QDy<j%S-`t@NbQY4RcSJYzz!<y-S>+)d z#Yvoly$NSobug<f0oepuwaV&3U~j_93=hCs$fj(<0USg-Hp$vFPCOkL>4eVcif-tE zo`^#{Mq>=dVjRZfHB5kfdh_V^FE@X=@{4@&;+r2%eDT9)pUUr!t(!hwvuv^T%V##z z-=4R4jgsj{udNqt{?|WRN3F4*KK6J;l3Qc^89t_)H-+-p(`Ah5blWbe7f3h0{X53v zo$A=QCG~$YE8T^WSIzeZZRjbpxM$%LJS)d$cZBSA7szgRhwOGQ$Zk6!yO{!eS0B1> z`vbk}@jx~6>znHWi{8nUDAS03Y)+v4Fkcme^3NBhsDYds0u_%^YR^)tqLSWInxN+H z`$a3c!qtdbiE8zMG9k@tuX;z_vR@7MN*nl@T3pRpl|O=AMTt==8-uQ>L$i$LPM@n( zHSQc&2mfV)w=0G5{uy<6rjhidQA&8`W$g*+D~mEteB-umYcxHxyyx9=mJzq*)T5b} zlhO3$QZBkJmucj&`y3f@%kk&qnR1lT^c<6~xaBA#Zu^)eK2ffKx#hh0Os1S=G(G3@ z*W7ZJk@V%FGsngnO<!-c5}7%+_5`=(GsP&{kWshgqp~h<O~_b&{w24~j?O%0WQtaF z*3nEgD%z}IZfnjGvFNN3i#9Sv%o<H!J|=6evL~c(c}(WWS)*>tXNg>lE0k`_XNp`* z=E%h;St4go$XGs8tYR{cWe!zL*3nFni^&|hn5@U5Jt1SwnIjitj;8N<6<6f^?1P&L zJY>=Hj{1?h&ggkdZS27}5e?20ovjDK_$p!=bK9sn<O>K-O+wFEGFb8K6h^l@>T2^P z`?P$QC)?a|rT0U!Y=|{OzBc*P8RVIDM*3F(td+^*-dPs4Qn+^RFVT6%rpVodEcsbW z$OB&gcQdSQmWTJu0+DCIvaBrAUad&;f24L&pI-8^=X(zENb&ldT7ToXzji|1XvF$x zRt*`XY27mNN>7(Yy3wj}K9}a1mi$EZ($c~_we8w}v82(%Ln|SF64-5}GAV~#q@TUw z>=u_C(`M(?+NkQ9jIF=mt#!;O!7MY<cgtGNU_aLW7dzm#>;LT{Mx6ld3!{60w$^;f zA@{AGy{l;~rRP!?7>i43#f@r(wP5p@r9|5Lb}XKLh|D+Vr%fxN-SJQj8F|4>H!D<Y zmA;EczOvffv_++~9G)J%Jd37zl-J68t7=-s2(728<}qePY906{ZV8$egViX-*RO~o zO^I+XFQt)^p!DQFGD%Pd)9mn53M+mGd=zbQzPcq<jVX1N$H_~S6t%A6s}^=Gl{?c? z4yB%wNFK6$fKsnX5v6;hMt6GhKA=&4&!LD?9CdhJWsa=cHz<A84V7emho-MO@UN?` zv_x4ft5yQjSM9!3wyRD%7pb-Mb55Z7?$48o(xS5eQxXq`y<Bqg?)wzwnPv-PSn=UQ zhuqx#C9(X`U-=WY@&{Gr&u+>eiIhKSD1U&jPd;v)=I5>jrWT;f)-Zq@UIqCQALNgo z$Y1J^KP7R>L4&;{9aeIe?`O+5U^~>Ik=}&He0v)IU0G32>)A<p<g0Ce(j{A;oXW>H za%);{y-1%_IuFtYZ_0MEsL}x)(Me%fI~)C%X;oVFWxO8-U=ZZ~?<kOkLf=`TyQ9#h zQRd@qyocpjjZ|#Mr#OQf_{kU;t3B_G&CUK2gAjI0{tkouNdoy31M;PM`QE#HYhAuZ zE#F?2FD1(tgXIgY@-<ZXI%$hKba5fy)06MS?QcX^gAnv+YJDe5zPKabosloPtYW|9 z`vmeFzdZ4MrW;!?k2NRvp#K4R+*Y0fl}9t>AwqcwP@dY8N515VEqO{yo|=;9p5$34 zc|<9IxXN=g@+^!z$s&)Z$b${?pn^OaAfM{X2l4WWxqON(pQXxYr1IJ4vSCC|K1-C( z2<78F`Or>2A(D@M<f9(>fJQ!4kq<rOgADm_LT-}Fjc>U_E%%P)ez4r*mHW1G2UhN~ z%AHiXTPk-%<*ujP*_6ANa)(mxLdu;+xtn;s6)$pIP;TbQ4LiA&6fj=y5XoI2xzl6a z^O9REavMc%m9&^Fw?O0$hxE3kM{V_rr8_iXuF<NV7G%_4rUm5dqEOF4WH?F*gE6a~ zR@6AZO!Lggjwo!r!iFlWUm0)guBX*D8ZFm+^BIi4g?I2S78rw;YbE_YWN-ymVwJIS zxmLnRuCEozv5oOhj9Vlz0zTvq9-L+T9M0ncF5(g{;|i|g8m=3QKhz31e`c^L^*j#o zn206ViQU+PeYk{Qk&lj50R*5BVo(QN&<)+u6DfETtFZ=au^z{94u9ZJPio*}29z8e zwRjP6NI-8SVk#D62X<jM_TnObLM|^(AMisSR6>39Krh500h2Hv8?gzSk>*8#f5*T% z{DD957>YL=gm5%NbF@G!48jbo#A<wmwK$3!$icbJ8$QT|@~DoN(E%ON8KW`R$;*1A zViUIDTiim<oa`C=kO!4e58crVaY(=<%*RG-#ujYF_xKUsx!4foh9AOE3!UKX%1bx& zz<4acR(yi(*om`vf}**(V}cS0ft<bKF&+~z5tFeT2XP5kaSb;h|Bq5AYC-;HeqCS6 zzdi%;n2aS@hUJhy+kFT(;p4~F!x#Rjh!*IJ0Z7Im%*Hw#!BHH;3EaVB1aYb@j$k-Y z$G;Kf%RnOfq8|oeCYE3)_Fyme<1*yWKg-|GE{5WeKlwaidRp;D+GN$}-bgD_WZrbP zSt*6m2t$F8fG6|D8_ODNXOf>|+)wGQwB@W2r$j21m8$%@?m%!KBKvt&|7Kpb?B*F4 znrQ{g*ei%qBC<=UMA7@Z@RC-*sMA!-UovBf%GoVpw0cP^*f}F%ban~JI_7WYWxLPD z9#-JYSV6WssYDaN>;f14H~Y-Jg1=eL+;itsODdXu%6^5}M<V)fA`$)6(sx=_gL?yi zv%>5n!##m)my7wE$Z#*fTF&{Dddj`BzsWTF$iz@k*+nfTo5+-uTXQi_4UH8U>HAY} zpG)uE$0*f|e9g7z(rb?N?zvQ$P13qt#6me_<*zZGhDNKhZ6N2FV%(K3L2J;#$*m+G zdvp92;8;Ccj<a)BT6~-;K59wpj^@;3no>V-eGiP|0R+f5cMiZiiStlju4DblXMj>t z@uT^(gC<WZt(+>fYUJsLoGG+ICUQod%o%gKalE-U)5-eG^F;{nH1Cuq9-QRZ$Wv^f z$_7qGUpDeLHnKb$*_@3m!$KR`$S^kY8XLKXjg-H4u!oIY%|^<9KlmRuvMd`|4^eF3 zHI!x}<*EHXY-G;?eD_73(EWjp+{s49vXRn!D#}LgVj~Mpk&Tqbe=wWnrA$tYq%E&0 zZI6AK!%Las8k=j5O||EJA@io$^USf=U1MLHV{h2=j!MeuiB2oixu44D414B&8JlS@ zctpnDbd9w&$7Z?40?e`5cFMz&a*k_kt~sXL%b8<y?d8m|d5MuubDzHcOctDPFKCV# z_JZcvTlRwH*xPo>L$cgE_Siugd)H2B&RgIbYirJX&z@(FEo6*VfTnzXKo)%8HFK^x zw#Z)44Af$KL37>`d(52ofotq*ih@hE(v8~lGm7NnN|m3h8D1|yO9grWH3zZOuEluV zn3vUssZvz25kXuhsb*`C&viVkrmBTe)$UR=MvUiUerm@hjOP>1un9C25HrcF&iPww z+nhaF@m&;U<t140GR$Db8!(?0Z$^FAxfD{p*Rt;NtXuvHVR6>G9s#VoF4cT3>ken# zzq0O8tb01^y^0B}JDm0ILN#;U>@`0s_wjyct_?D_x6zt<PF?*a&r`S6HX60tYF@^^ zw%T-OF%OP#grY2Fu~q}IQG5vu3u6cueyv8M5Ep82;GlXeEe2GoM{fbA;%J3JzLRna zpN^y1gYFzN2{?eyv2-fY#jR=NIZb&{nfy8-#L_o7r9l2?Skcj?6Yru6x}rOJpcmqh z0?r&K+G$gbTkSL-rzh=IPpXjhF2feN|3C+W2&X{$&Sg-lI42kAA#^Yh9m>fGMavR> z457s`9<8F;EUbKilEb<>v{6x@5ob!|XiSR?*PTsiXktPeI>HETOOSA;D|Ha-da@uw zdJ}NGoXCFRRx)4fK!*|RFWM12>8llRa!N&Y)IhKCG!Y@yZU)v;^y_gDpP}1tygWdj z3DjBCMl8Oa%0^%aK_2tJ2f9UjZH&>qz2^IjKuiBf`bE*O`$f_p8V|caB>kXcu=_#M z{V9<nqx&P>o++@qJ<{E|0JFQ}WI*~k6=C;tq<=FJlOX*Y>DPP<yIUjOnG)U^-5Ke| z420c{k?zYEu)8nPZ7B!4+aleSQIPJ6b1pB^P5B0PH$}Q96=8Qzq+2o(cDF>jBj3U9 zjz~A80_<*x^gf)hdmqy4I0L)aA-#<{pts>PyBpHYSO&YBA>9if-;C}B-3s%++p>EV z(wjI4yEh@dh%nf_2<bh{g57(NUc+tJy$0zm)J7fu1gp0oy@VOCdkNAzxCFa*AiaW@ zr~mWgdK}Aeu{COS)K(iWb=0~z?Za9QZFk;`$<P0BjdPkk?Eil?j${A-YmIYKv!sLj z|EtDTsY9)j{^@^P<D{wPp?DkJI#Yv^I%^%$a(2-!2Nt}hnxBZvZCLXrkkp;4hBlmg z;d}?ms4-j%@sJNXx(wGQX(d}3=Z0%RT6k;Y{&1~zA$gE<V`w?$^?c=(uX~hN<WK8e zjWgPh(5e@ekCaAth*qw(icu2sR8izpiq!U1lv=&28H+|}f!>dE)Zzchi#4{6&<1Js zh8ksF)i!$Y4H)D5SG6z?`IAbXBeiPY^7S4*w=!ys)Rt+zN*L!xYDa@VAGTbPzp?aI zw+|Kh8!B%{tWbJIuTc8cTA|E+(bzRgYoc`;Yj}^=>U!{3ON>UNwf-LRsn4d-TD0cZ z&A2vNdq<0iG@N5dsf8K`$7sXr+gDnN0bx;|)bSXvnk{Z=bt`qoYw`qvIn1d2uH|J! ze&m+;z_gUH?9Pi3K2wV*w~uoy%Kre*y@{pYPtL#nd(OCA0F-dDyBk1TfJ<9pF8fjh z9(f=0o_Xryv})tE#%i%wl}$>@Yx!w`<mGmKJn3i>DS266-sya8p;p@I6s?rTQ!jP4 zOw^)OEjr4GnWUBRbR<6>Wb~Y*1vrW=A1?o0xa<bkd1`{SDtVWU@Gv4)sh=8kR;gS5 z?_SuxQuF6(U+0${l{L$b%X{AG#?`r6FHP|^n#|KeLl$sgaq&}@^3HWl*~+^_?lkXQ zkCb1G8S}Jt1($Lu_?%J6qJ~n;d_QEgny(f2nB-H!IB{GpSk%5lV1A10N?F`|-<vsQ zK|3W~#PmFi8%^hHCI6Zm{mP6qW^P+D=a#-+>7CkJXsvgBwkc^?X4=A4*`}oFl_}-Y zY*SKYGNoLYZOY17QofaK%E&A!=VY5QB1_8Y*`^FnPZ?}(@8oP!M!S`cm2!NxDKpB* zN;xXqlo@4Yr5u`V%8W9yQVz&AWkwlUDHF3znNdbo%3j%~%qSx(WtVJIW|WbY^5tw( zW|WbYvSqd@Gs>uhSw>B=Ng3r<MpnxD*`~}WBP(UCY*S{Gk(IJ)wkb2p$VwTRZOV)? zvQk#aHf2T`St(0rn=+$}tdx#yQ)ZNrl`<&Xlo@4Yr3}b6WkwklG0Vt5o0OH^%E(IT zlWoe3GO|)?nNz0UT&t{9NsC>?GYVUlYU4dTFK|d36F<~ycs}L>Vq^b@+EmW~E`~<S z6<Q-}+JY5Yyfc<*bx{xX(EttcA{wDFnxH9~;U(}rYo!HRq7_=B4cej|UWTl-1MeNt z37ydeUC|9PzlX|AwVsUhLLB0e0Qs}ylHgA_eVNwJHC^T<8$DNQFV0`3^~>4gW5*kE zeH~y7TctU@?y&1hKjYylZIo9Tmbl!_7`0j(r&T#`JY21fQrjEDKGK|OcjMQOwB#3+ zZpF=o7hMiE-(_L@=P0+BcPq!0R?0D@4WB{pQ`&LIvKdbage#4?(cDz|j^8Hy9yeC6 z(W?1p&MfWj8ZACYsi?@x;gze_trk`-reRcAR7}<SVO8r!)DMe|jA#&9w@Uqb)gr5; KjY`!fYyS_@zN2OU delta 27909 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