Project setup

This commit is contained in:
Stephen White 2018-05-20 14:53:34 +10:00
parent 5052645af8
commit 6dc9452b2a
166 changed files with 148197 additions and 0 deletions

73
3rdPartyFiles.txt Normal file
View File

@ -0,0 +1,73 @@
Brian Sidebotham's - Raspberry-Pi Bare Metal Tutorials
https://github.com/BrianSidebotham/arm-tutorial-rpi
armc-cstartup.c
armc-cstubs.c
rpi-base.h
rpi-interrupts.h
rpi-interrupts.c
rpi-mailbox.h
rpi-mailbox.c
David Banks' PiTubeDirect (GNU General Public License v3.0)
https://github.com/hoglet67/PiTubeDirect
defs.h
cache.h
cache.c
armc-start.S
startup.h
rpi-aux.h
rpi-aux.c
rpi-gpio.h (added GpioDetect)
rpi-gpio.c
rpi-mailbox-interface.h (Added some missing ones)
rpi-mailbox-interface.c
linker.ld
R Stange's USPi
https://github.com/rsta2/uspi
uspi\*.*
exception.c
interrupt.h
interrupt.c
Timer.h
Timer.cpp
R Stange's modified version of John Cronin's EMMC controller
emmc.h
emmc.cpp
ChaN's Generic FAT file system R0.12b
http://elm-chan.org/fsw/ff/00index_e.html
ff.h
ff.cpp
diskio.h
diskio.cpp
Sean Barrett's stb_image (MIT) (used for loading PNGs)
https://github.com/nothings/stb
stb_image.h
FFmpeg (LGPL v2.1+) (used for the font)
https://github.com/FFmpeg/FFmpeg
xga_font_data.h
xga_font_data.c
Pete Rittwage's (Markus Brenner's) NIB lib
https://c64preservation.com/svn/nibtools/trunk
gcr.h
gcr.cpp
prot.h
prot.cpp
Marcus Geelnard's LZ77 coder/decoder (GNU General Public License, version 2 or later)
https://c64preservation.com/svn/nibtools/trunk/lz.c
lz.h
lz.c
nbla000's CBM-FileBrowser_v1.6
http://www.nightfallcrew.com/21/08/2013/cbm-filebrowser-v1-6-by-nbla000/

259
CBMFont.h Normal file
View File

@ -0,0 +1,259 @@
const long int CMBFont_size = 4096;
const unsigned char CMBFont[4096] = {
0x3C, 0x66, 0x6E, 0x6E, 0x60, 0x62, 0x3C, 0x00, 0x18, 0x3C, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00,
0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00,
0x78, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0x78, 0x00, 0x7E, 0x60, 0x60, 0x78, 0x60, 0x60, 0x7E, 0x00,
0x7E, 0x60, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00, 0x3C, 0x66, 0x60, 0x6E, 0x66, 0x66, 0x3C, 0x00,
0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00,
0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x6C, 0x38, 0x00, 0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66, 0x00,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00, 0x63, 0x77, 0x7F, 0x6B, 0x63, 0x63, 0x63, 0x00,
0x66, 0x76, 0x7E, 0x7E, 0x6E, 0x66, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00,
0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x0E, 0x00,
0x7C, 0x66, 0x66, 0x7C, 0x78, 0x6C, 0x66, 0x00, 0x3C, 0x66, 0x60, 0x3C, 0x06, 0x66, 0x3C, 0x00,
0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00,
0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00,
0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00,
0x7E, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00,
0x0C, 0x12, 0x30, 0x7C, 0x30, 0x62, 0xFC, 0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00,
0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x00, 0x10, 0x30, 0x7F, 0x7F, 0x30, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00,
0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0xFF, 0x66, 0xFF, 0x66, 0x66, 0x00,
0x18, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x18, 0x00, 0x62, 0x66, 0x0C, 0x18, 0x30, 0x66, 0x46, 0x00,
0x3C, 0x66, 0x3C, 0x38, 0x67, 0x66, 0x3F, 0x00, 0x06, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00,
0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00,
0x3C, 0x66, 0x6E, 0x76, 0x66, 0x66, 0x3C, 0x00, 0x18, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7E, 0x00,
0x3C, 0x66, 0x06, 0x0C, 0x30, 0x60, 0x7E, 0x00, 0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00,
0x06, 0x0E, 0x1E, 0x66, 0x7F, 0x06, 0x06, 0x00, 0x7E, 0x60, 0x7C, 0x06, 0x06, 0x66, 0x3C, 0x00,
0x3C, 0x66, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00, 0x7E, 0x66, 0x0C, 0x18, 0x18, 0x18, 0x18, 0x00,
0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x3C, 0x66, 0x66, 0x3E, 0x06, 0x66, 0x3C, 0x00,
0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30,
0x0E, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00,
0x70, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x70, 0x00, 0x3C, 0x66, 0x06, 0x0C, 0x18, 0x00, 0x18, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x3E, 0x7F, 0x7F, 0x1C, 0x3E, 0x00,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x00, 0x00, 0x00, 0xE0, 0xF0, 0x38, 0x18, 0x18,
0x18, 0x18, 0x1C, 0x0F, 0x07, 0x00, 0x00, 0x00, 0x18, 0x18, 0x38, 0xF0, 0xE0, 0x00, 0x00, 0x00,
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03,
0x03, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0xC0, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
0xFF, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x3C, 0x7E, 0x7E, 0x7E, 0x7E, 0x3C, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x36, 0x7F, 0x7F, 0x7F, 0x3E, 0x1C, 0x08, 0x00,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x07, 0x0F, 0x1C, 0x18, 0x18,
0xC3, 0xE7, 0x7E, 0x3C, 0x3C, 0x7E, 0xE7, 0xC3, 0x00, 0x3C, 0x7E, 0x66, 0x66, 0x7E, 0x3C, 0x00,
0x18, 0x18, 0x66, 0x66, 0x18, 0x18, 0x3C, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x1C, 0x08, 0x00, 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x18, 0x18, 0x18,
0xC0, 0xC0, 0x30, 0x30, 0xC0, 0xC0, 0x30, 0x30, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x00, 0x00, 0x03, 0x3E, 0x76, 0x36, 0x36, 0x00, 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0x33, 0x33, 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x18, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xF8, 0xF8, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x1F, 0x1F, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x18, 0x18, 0x18,
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0,
0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x00, 0x00, 0x00,
0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F,
0xC3, 0x99, 0x91, 0x91, 0x9F, 0x99, 0xC3, 0xFF, 0xE7, 0xC3, 0x99, 0x81, 0x99, 0x99, 0x99, 0xFF,
0x83, 0x99, 0x99, 0x83, 0x99, 0x99, 0x83, 0xFF, 0xC3, 0x99, 0x9F, 0x9F, 0x9F, 0x99, 0xC3, 0xFF,
0x87, 0x93, 0x99, 0x99, 0x99, 0x93, 0x87, 0xFF, 0x81, 0x9F, 0x9F, 0x87, 0x9F, 0x9F, 0x81, 0xFF,
0x81, 0x9F, 0x9F, 0x87, 0x9F, 0x9F, 0x9F, 0xFF, 0xC3, 0x99, 0x9F, 0x91, 0x99, 0x99, 0xC3, 0xFF,
0x99, 0x99, 0x99, 0x81, 0x99, 0x99, 0x99, 0xFF, 0xC3, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xC3, 0xFF,
0xE1, 0xF3, 0xF3, 0xF3, 0xF3, 0x93, 0xC7, 0xFF, 0x99, 0x93, 0x87, 0x8F, 0x87, 0x93, 0x99, 0xFF,
0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x81, 0xFF, 0x9C, 0x88, 0x80, 0x94, 0x9C, 0x9C, 0x9C, 0xFF,
0x99, 0x89, 0x81, 0x81, 0x91, 0x99, 0x99, 0xFF, 0xC3, 0x99, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xFF,
0x83, 0x99, 0x99, 0x83, 0x9F, 0x9F, 0x9F, 0xFF, 0xC3, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xF1, 0xFF,
0x83, 0x99, 0x99, 0x83, 0x87, 0x93, 0x99, 0xFF, 0xC3, 0x99, 0x9F, 0xC3, 0xF9, 0x99, 0xC3, 0xFF,
0x81, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xFF,
0x99, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xE7, 0xFF, 0x9C, 0x9C, 0x9C, 0x94, 0x80, 0x88, 0x9C, 0xFF,
0x99, 0x99, 0xC3, 0xE7, 0xC3, 0x99, 0x99, 0xFF, 0x99, 0x99, 0x99, 0xC3, 0xE7, 0xE7, 0xE7, 0xFF,
0x81, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x81, 0xFF, 0xC3, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xC3, 0xFF,
0xF3, 0xED, 0xCF, 0x83, 0xCF, 0x9D, 0x03, 0xFF, 0xC3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xC3, 0xFF,
0xFF, 0xE7, 0xC3, 0x81, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, 0xEF, 0xCF, 0x80, 0x80, 0xCF, 0xEF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, 0xFF, 0xE7, 0xFF,
0x99, 0x99, 0x99, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0x99, 0x00, 0x99, 0x00, 0x99, 0x99, 0xFF,
0xE7, 0xC1, 0x9F, 0xC3, 0xF9, 0x83, 0xE7, 0xFF, 0x9D, 0x99, 0xF3, 0xE7, 0xCF, 0x99, 0xB9, 0xFF,
0xC3, 0x99, 0xC3, 0xC7, 0x98, 0x99, 0xC0, 0xFF, 0xF9, 0xF3, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xF3, 0xE7, 0xCF, 0xCF, 0xCF, 0xE7, 0xF3, 0xFF, 0xCF, 0xE7, 0xF3, 0xF3, 0xF3, 0xE7, 0xCF, 0xFF,
0xFF, 0x99, 0xC3, 0x00, 0xC3, 0x99, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0x81, 0xE7, 0xE7, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0xCF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0xFF, 0xFF, 0xFC, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0xFF,
0xC3, 0x99, 0x91, 0x89, 0x99, 0x99, 0xC3, 0xFF, 0xE7, 0xE7, 0xC7, 0xE7, 0xE7, 0xE7, 0x81, 0xFF,
0xC3, 0x99, 0xF9, 0xF3, 0xCF, 0x9F, 0x81, 0xFF, 0xC3, 0x99, 0xF9, 0xE3, 0xF9, 0x99, 0xC3, 0xFF,
0xF9, 0xF1, 0xE1, 0x99, 0x80, 0xF9, 0xF9, 0xFF, 0x81, 0x9F, 0x83, 0xF9, 0xF9, 0x99, 0xC3, 0xFF,
0xC3, 0x99, 0x9F, 0x83, 0x99, 0x99, 0xC3, 0xFF, 0x81, 0x99, 0xF3, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF,
0xC3, 0x99, 0x99, 0xC3, 0x99, 0x99, 0xC3, 0xFF, 0xC3, 0x99, 0x99, 0xC1, 0xF9, 0x99, 0xC3, 0xFF,
0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xE7, 0xE7, 0xCF,
0xF1, 0xE7, 0xCF, 0x9F, 0xCF, 0xE7, 0xF1, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0x81, 0xFF, 0xFF, 0xFF,
0x8F, 0xE7, 0xF3, 0xF9, 0xF3, 0xE7, 0x8F, 0xFF, 0xC3, 0x99, 0xF9, 0xF3, 0xE7, 0xFF, 0xE7, 0xFF,
0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xF7, 0xE3, 0xC1, 0x80, 0x80, 0xE3, 0xC1, 0xFF,
0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF,
0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xFF, 0xFF, 0xFF, 0x1F, 0x0F, 0xC7, 0xE7, 0xE7,
0xE7, 0xE7, 0xE3, 0xF0, 0xF8, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0xC7, 0x0F, 0x1F, 0xFF, 0xFF, 0xFF,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x00, 0x00, 0x3F, 0x1F, 0x8F, 0xC7, 0xE3, 0xF1, 0xF8, 0xFC,
0xFC, 0xF8, 0xF1, 0xE3, 0xC7, 0x8F, 0x1F, 0x3F, 0x00, 0x00, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x00, 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFF, 0xC3, 0x81, 0x81, 0x81, 0x81, 0xC3, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xC9, 0x80, 0x80, 0x80, 0xC1, 0xE3, 0xF7, 0xFF,
0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0xFF, 0xFF, 0xFF, 0xF8, 0xF0, 0xE3, 0xE7, 0xE7,
0x3C, 0x18, 0x81, 0xC3, 0xC3, 0x81, 0x18, 0x3C, 0xFF, 0xC3, 0x81, 0x99, 0x99, 0x81, 0xC3, 0xFF,
0xE7, 0xE7, 0x99, 0x99, 0xE7, 0xE7, 0xC3, 0xFF, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9,
0xF7, 0xE3, 0xC1, 0x80, 0xC1, 0xE3, 0xF7, 0xFF, 0xE7, 0xE7, 0xE7, 0x00, 0x00, 0xE7, 0xE7, 0xE7,
0x3F, 0x3F, 0xCF, 0xCF, 0x3F, 0x3F, 0xCF, 0xCF, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7,
0xFF, 0xFF, 0xFC, 0xC1, 0x89, 0xC9, 0xC9, 0xFF, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
0xFF, 0xFF, 0xFF, 0xFF, 0x33, 0x33, 0xCC, 0xCC, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F,
0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xE7, 0xE7, 0xE7, 0xE0, 0xE0, 0xE7, 0xE7, 0xE7,
0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xE7, 0xE7, 0xE7, 0xE0, 0xE0, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x07, 0x07, 0xE7, 0xE7, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0x07, 0x07, 0xE7, 0xE7, 0xE7,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F,
0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0xE7, 0x07, 0x07, 0xFF, 0xFF, 0xFF,
0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0,
0x3C, 0x66, 0x6E, 0x6E, 0x60, 0x62, 0x3C, 0x00, 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00,
0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, 0x3C, 0x60, 0x60, 0x60, 0x3C, 0x00,
0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00,
0x00, 0x0E, 0x18, 0x3E, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x7C,
0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x00, 0x00, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3C, 0x00,
0x00, 0x06, 0x00, 0x06, 0x06, 0x06, 0x06, 0x3C, 0x00, 0x60, 0x60, 0x6C, 0x78, 0x6C, 0x66, 0x00,
0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x66, 0x7F, 0x7F, 0x6B, 0x63, 0x00,
0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00,
0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x06,
0x00, 0x00, 0x7C, 0x66, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x00,
0x00, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x0E, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00,
0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x3E, 0x36, 0x00,
0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x0C, 0x78,
0x00, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x7E, 0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00,
0x0C, 0x12, 0x30, 0x7C, 0x30, 0x62, 0xFC, 0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00,
0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x00, 0x10, 0x30, 0x7F, 0x7F, 0x30, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00,
0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0xFF, 0x66, 0xFF, 0x66, 0x66, 0x00,
0x18, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x18, 0x00, 0x62, 0x66, 0x0C, 0x18, 0x30, 0x66, 0x46, 0x00,
0x3C, 0x66, 0x3C, 0x38, 0x67, 0x66, 0x3F, 0x00, 0x06, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00,
0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00,
0x3C, 0x66, 0x6E, 0x76, 0x66, 0x66, 0x3C, 0x00, 0x18, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7E, 0x00,
0x3C, 0x66, 0x06, 0x0C, 0x30, 0x60, 0x7E, 0x00, 0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00,
0x06, 0x0E, 0x1E, 0x66, 0x7F, 0x06, 0x06, 0x00, 0x7E, 0x60, 0x7C, 0x06, 0x06, 0x66, 0x3C, 0x00,
0x3C, 0x66, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00, 0x7E, 0x66, 0x0C, 0x18, 0x18, 0x18, 0x18, 0x00,
0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x3C, 0x66, 0x66, 0x3E, 0x06, 0x66, 0x3C, 0x00,
0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30,
0x0E, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00,
0x70, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x70, 0x00, 0x3C, 0x66, 0x06, 0x0C, 0x18, 0x00, 0x18, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00,
0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00,
0x78, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0x78, 0x00, 0x7E, 0x60, 0x60, 0x78, 0x60, 0x60, 0x7E, 0x00,
0x7E, 0x60, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00, 0x3C, 0x66, 0x60, 0x6E, 0x66, 0x66, 0x3C, 0x00,
0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00,
0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x6C, 0x38, 0x00, 0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66, 0x00,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00, 0x63, 0x77, 0x7F, 0x6B, 0x63, 0x63, 0x63, 0x00,
0x66, 0x76, 0x7E, 0x7E, 0x6E, 0x66, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00,
0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x0E, 0x00,
0x7C, 0x66, 0x66, 0x7C, 0x78, 0x6C, 0x66, 0x00, 0x3C, 0x66, 0x60, 0x3C, 0x06, 0x66, 0x3C, 0x00,
0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00,
0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00,
0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00,
0x7E, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00, 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x18, 0x18, 0x18,
0xC0, 0xC0, 0x30, 0x30, 0xC0, 0xC0, 0x30, 0x30, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x99, 0xCC, 0x66, 0x33, 0x99, 0xCC, 0x66,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0x99, 0x33, 0x66, 0xCC, 0x99, 0x33, 0x66,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x18, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xF8, 0xF8, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x1F, 0x1F, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x18, 0x18, 0x18,
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
0x01, 0x03, 0x06, 0x6C, 0x78, 0x70, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0,
0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x00, 0x00, 0x00,
0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F,
0xC3, 0x99, 0x91, 0x91, 0x9F, 0x99, 0xC3, 0xFF, 0xFF, 0xFF, 0xC3, 0xF9, 0xC1, 0x99, 0xC1, 0xFF,
0xFF, 0x9F, 0x9F, 0x83, 0x99, 0x99, 0x83, 0xFF, 0xFF, 0xFF, 0xC3, 0x9F, 0x9F, 0x9F, 0xC3, 0xFF,
0xFF, 0xF9, 0xF9, 0xC1, 0x99, 0x99, 0xC1, 0xFF, 0xFF, 0xFF, 0xC3, 0x99, 0x81, 0x9F, 0xC3, 0xFF,
0xFF, 0xF1, 0xE7, 0xC1, 0xE7, 0xE7, 0xE7, 0xFF, 0xFF, 0xFF, 0xC1, 0x99, 0x99, 0xC1, 0xF9, 0x83,
0xFF, 0x9F, 0x9F, 0x83, 0x99, 0x99, 0x99, 0xFF, 0xFF, 0xE7, 0xFF, 0xC7, 0xE7, 0xE7, 0xC3, 0xFF,
0xFF, 0xF9, 0xFF, 0xF9, 0xF9, 0xF9, 0xF9, 0xC3, 0xFF, 0x9F, 0x9F, 0x93, 0x87, 0x93, 0x99, 0xFF,
0xFF, 0xC7, 0xE7, 0xE7, 0xE7, 0xE7, 0xC3, 0xFF, 0xFF, 0xFF, 0x99, 0x80, 0x80, 0x94, 0x9C, 0xFF,
0xFF, 0xFF, 0x83, 0x99, 0x99, 0x99, 0x99, 0xFF, 0xFF, 0xFF, 0xC3, 0x99, 0x99, 0x99, 0xC3, 0xFF,
0xFF, 0xFF, 0x83, 0x99, 0x99, 0x83, 0x9F, 0x9F, 0xFF, 0xFF, 0xC1, 0x99, 0x99, 0xC1, 0xF9, 0xF9,
0xFF, 0xFF, 0x83, 0x99, 0x9F, 0x9F, 0x9F, 0xFF, 0xFF, 0xFF, 0xC1, 0x9F, 0xC3, 0xF9, 0x83, 0xFF,
0xFF, 0xE7, 0x81, 0xE7, 0xE7, 0xE7, 0xF1, 0xFF, 0xFF, 0xFF, 0x99, 0x99, 0x99, 0x99, 0xC1, 0xFF,
0xFF, 0xFF, 0x99, 0x99, 0x99, 0xC3, 0xE7, 0xFF, 0xFF, 0xFF, 0x9C, 0x94, 0x80, 0xC1, 0xC9, 0xFF,
0xFF, 0xFF, 0x99, 0xC3, 0xE7, 0xC3, 0x99, 0xFF, 0xFF, 0xFF, 0x99, 0x99, 0x99, 0xC1, 0xF3, 0x87,
0xFF, 0xFF, 0x81, 0xF3, 0xE7, 0xCF, 0x81, 0xFF, 0xC3, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xC3, 0xFF,
0xF3, 0xED, 0xCF, 0x83, 0xCF, 0x9D, 0x03, 0xFF, 0xC3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xC3, 0xFF,
0xFF, 0xE7, 0xC3, 0x81, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, 0xEF, 0xCF, 0x80, 0x80, 0xCF, 0xEF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, 0xFF, 0xE7, 0xFF,
0x99, 0x99, 0x99, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0x99, 0x00, 0x99, 0x00, 0x99, 0x99, 0xFF,
0xE7, 0xC1, 0x9F, 0xC3, 0xF9, 0x83, 0xE7, 0xFF, 0x9D, 0x99, 0xF3, 0xE7, 0xCF, 0x99, 0xB9, 0xFF,
0xC3, 0x99, 0xC3, 0xC7, 0x98, 0x99, 0xC0, 0xFF, 0xF9, 0xF3, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xF3, 0xE7, 0xCF, 0xCF, 0xCF, 0xE7, 0xF3, 0xFF, 0xCF, 0xE7, 0xF3, 0xF3, 0xF3, 0xE7, 0xCF, 0xFF,
0xFF, 0x99, 0xC3, 0x00, 0xC3, 0x99, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0x81, 0xE7, 0xE7, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0xCF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0xFF, 0xFF, 0xFC, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0xFF,
0xC3, 0x99, 0x91, 0x89, 0x99, 0x99, 0xC3, 0xFF, 0xE7, 0xE7, 0xC7, 0xE7, 0xE7, 0xE7, 0x81, 0xFF,
0xC3, 0x99, 0xF9, 0xF3, 0xCF, 0x9F, 0x81, 0xFF, 0xC3, 0x99, 0xF9, 0xE3, 0xF9, 0x99, 0xC3, 0xFF,
0xF9, 0xF1, 0xE1, 0x99, 0x80, 0xF9, 0xF9, 0xFF, 0x81, 0x9F, 0x83, 0xF9, 0xF9, 0x99, 0xC3, 0xFF,
0xC3, 0x99, 0x9F, 0x83, 0x99, 0x99, 0xC3, 0xFF, 0x81, 0x99, 0xF3, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF,
0xC3, 0x99, 0x99, 0xC3, 0x99, 0x99, 0xC3, 0xFF, 0xC3, 0x99, 0x99, 0xC1, 0xF9, 0x99, 0xC3, 0xFF,
0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xE7, 0xE7, 0xCF,
0xF1, 0xE7, 0xCF, 0x9F, 0xCF, 0xE7, 0xF1, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0x81, 0xFF, 0xFF, 0xFF,
0x8F, 0xE7, 0xF3, 0xF9, 0xF3, 0xE7, 0x8F, 0xFF, 0xC3, 0x99, 0xF9, 0xF3, 0xE7, 0xFF, 0xE7, 0xFF,
0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xE7, 0xC3, 0x99, 0x81, 0x99, 0x99, 0x99, 0xFF,
0x83, 0x99, 0x99, 0x83, 0x99, 0x99, 0x83, 0xFF, 0xC3, 0x99, 0x9F, 0x9F, 0x9F, 0x99, 0xC3, 0xFF,
0x87, 0x93, 0x99, 0x99, 0x99, 0x93, 0x87, 0xFF, 0x81, 0x9F, 0x9F, 0x87, 0x9F, 0x9F, 0x81, 0xFF,
0x81, 0x9F, 0x9F, 0x87, 0x9F, 0x9F, 0x9F, 0xFF, 0xC3, 0x99, 0x9F, 0x91, 0x99, 0x99, 0xC3, 0xFF,
0x99, 0x99, 0x99, 0x81, 0x99, 0x99, 0x99, 0xFF, 0xC3, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xC3, 0xFF,
0xE1, 0xF3, 0xF3, 0xF3, 0xF3, 0x93, 0xC7, 0xFF, 0x99, 0x93, 0x87, 0x8F, 0x87, 0x93, 0x99, 0xFF,
0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x81, 0xFF, 0x9C, 0x88, 0x80, 0x94, 0x9C, 0x9C, 0x9C, 0xFF,
0x99, 0x89, 0x81, 0x81, 0x91, 0x99, 0x99, 0xFF, 0xC3, 0x99, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xFF,
0x83, 0x99, 0x99, 0x83, 0x9F, 0x9F, 0x9F, 0xFF, 0xC3, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xF1, 0xFF,
0x83, 0x99, 0x99, 0x83, 0x87, 0x93, 0x99, 0xFF, 0xC3, 0x99, 0x9F, 0xC3, 0xF9, 0x99, 0xC3, 0xFF,
0x81, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xFF,
0x99, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xE7, 0xFF, 0x9C, 0x9C, 0x9C, 0x94, 0x80, 0x88, 0x9C, 0xFF,
0x99, 0x99, 0xC3, 0xE7, 0xC3, 0x99, 0x99, 0xFF, 0x99, 0x99, 0x99, 0xC3, 0xE7, 0xE7, 0xE7, 0xFF,
0x81, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x81, 0xFF, 0xE7, 0xE7, 0xE7, 0x00, 0x00, 0xE7, 0xE7, 0xE7,
0x3F, 0x3F, 0xCF, 0xCF, 0x3F, 0x3F, 0xCF, 0xCF, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7,
0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0x66, 0x33, 0x99, 0xCC, 0x66, 0x33, 0x99,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
0xFF, 0xFF, 0xFF, 0xFF, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x66, 0xCC, 0x99, 0x33, 0x66, 0xCC, 0x99,
0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xE7, 0xE7, 0xE7, 0xE0, 0xE0, 0xE7, 0xE7, 0xE7,
0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xE7, 0xE7, 0xE7, 0xE0, 0xE0, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x07, 0x07, 0xE7, 0xE7, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0x07, 0x07, 0xE7, 0xE7, 0xE7,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0xFE, 0xFC, 0xF9, 0x93, 0x87, 0x8F, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F,
0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0xE7, 0x07, 0x07, 0xFF, 0xFF, 0xFF,
0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0
};

202
DiskCaddy.cpp Normal file
View File

@ -0,0 +1,202 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#include "DiskCaddy.h"
#include "debug.h"
#include <string.h>
#include <stdlib.h>
#include "ff.h"
extern "C"
{
#include "rpi-gpio.h" // For SetACTLed
}
static const u32 screenPosXCaddySelections = 240;
static const u32 screenPosYCaddySelections = 280;
static char buffer[256] = { 0 };
static u32 white = RGBA(0xff, 0xff, 0xff, 0xff);
static u32 red = RGBA(0xff, 0, 0, 0xff);
bool DiskCaddy::Insert(const FILINFO* fileInfo, bool readOnly)
{
bool success;
FIL fp;
FRESULT res = f_open(&fp, fileInfo->fname, FA_READ);
if (res == FR_OK)
{
if (screen)
{
int y = screenPosYCaddySelections;
snprintf(buffer, 256, "Loading %s\r\n", fileInfo->fname);
screen->PrintText(false, screenPosXCaddySelections, y, buffer, RGBA(0xff, 0xff, 0xff, 0xff), red);
}
u32 bytesRead;
SetACTLed(true);
f_read(&fp, DiskImage::readBuffer, READBUFFER_SIZE, &bytesRead);
SetACTLed(false);
f_close(&fp);
DiskImage::DiskType diskType = DiskImage::GetDiskImageTypeViaExtention(fileInfo->fname);
switch (diskType)
{
case DiskImage::D64:
success = InsertD64(fileInfo, (unsigned char*)DiskImage::readBuffer, bytesRead, readOnly);
break;
case DiskImage::G64:
success = InsertG64(fileInfo, (unsigned char*)DiskImage::readBuffer, bytesRead, readOnly);
break;
case DiskImage::NIB:
success = InsertNIB(fileInfo, (unsigned char*)DiskImage::readBuffer, bytesRead, readOnly);
break;
case DiskImage::NBZ:
success = InsertNBZ(fileInfo, (unsigned char*)DiskImage::readBuffer, bytesRead, readOnly);
break;
default:
success = false;
break;
}
if (success)
{
DEBUG_LOG("Mounted into caddy %s - %d\r\n", fileInfo->fname, bytesRead);
}
}
else
{
DEBUG_LOG("Failed to open %s\r\n", fileInfo->fname);
success = false;
}
oldCaddyIndex = 0;
return success;
}
bool DiskCaddy::InsertD64(const FILINFO* fileInfo, unsigned char* diskImageData, unsigned size, bool readOnly)
{
DiskImage diskImage;
if (diskImage.OpenD64(fileInfo, diskImageData, size))
{
diskImage.SetReadOnly(readOnly);
disks.push_back(diskImage);
selectedIndex = disks.size() - 1;
return true;
}
return false;
}
bool DiskCaddy::InsertG64(const FILINFO* fileInfo, unsigned char* diskImageData, unsigned size, bool readOnly)
{
DiskImage diskImage;
if (diskImage.OpenG64(fileInfo, diskImageData, size))
{
diskImage.SetReadOnly(readOnly);
disks.push_back(diskImage);
//DEBUG_LOG("Disks size = %d\r\n", disks.size());
selectedIndex = disks.size() - 1;
return true;
}
return false;
}
bool DiskCaddy::InsertNIB(const FILINFO* fileInfo, unsigned char* diskImageData, unsigned size, bool readOnly)
{
DiskImage diskImage;
if (diskImage.OpenNIB(fileInfo, diskImageData, size))
{
// At the moment we cannot write out NIB files.
diskImage.SetReadOnly(true);// readOnly);
disks.push_back(diskImage);
selectedIndex = disks.size() - 1;
return true;
}
return false;
}
bool DiskCaddy::InsertNBZ(const FILINFO* fileInfo, unsigned char* diskImageData, unsigned size, bool readOnly)
{
DiskImage diskImage;
if (diskImage.OpenNBZ(fileInfo, diskImageData, size))
{
// At the moment we cannot write out NIB files.
diskImage.SetReadOnly(true);// readOnly);
disks.push_back(diskImage);
selectedIndex = disks.size() - 1;
return true;
}
return false;
}
void DiskCaddy::Display()
{
if (screen)
{
unsigned numberOfImages = GetNumberOfImages();
unsigned caddyIndex;
int y = screenPosYCaddySelections;
snprintf(buffer, 256, "Emulating\r\n");
screen->PrintText(false, screenPosXCaddySelections, y, buffer, RGBA(0xff, 0xff, 0xff, 0xff), red);
y += 16;
for (caddyIndex = 0; caddyIndex < numberOfImages; ++caddyIndex)
{
DiskImage* image = GetImage(caddyIndex);
const char* name = image->GetName();
if (name)
{
snprintf(buffer, 256, "%d %s\r\n", caddyIndex + 1, name);
screen->PrintText(false, screenPosXCaddySelections, y, buffer, RGBA(0xff, 0xff, 0xff, 0xff), red);
y += 16;
}
}
ShowSelectedImage(0);
}
}
void DiskCaddy::ShowSelectedImage(u32 index)
{
u32 x = screenPosXCaddySelections - 16;
u32 y = screenPosYCaddySelections + 16 + 16 * index;
snprintf(buffer, 256, "*");
screen->PrintText(false, x, y, buffer, white, red);
}
bool DiskCaddy::Update()
{
u32 y;
u32 x;
u32 caddyIndex = GetSelectedIndex();
if (caddyIndex != oldCaddyIndex)
{
if (screen)
{
x = screenPosXCaddySelections - 16;
y = screenPosYCaddySelections + 16 + 16 * oldCaddyIndex;
snprintf(buffer, 256, " ");
screen->PrintText(false, x, y, buffer, red, red);
oldCaddyIndex = caddyIndex;
ShowSelectedImage(oldCaddyIndex);
}
return true;
}
return false;
}

111
DiskCaddy.h Normal file
View File

@ -0,0 +1,111 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#ifndef DISKCADDY_H
#define DISKCADDY_H
#include <vector>
#include "DiskImage.h"
#include "Screen.h"
class DiskCaddy
{
public:
DiskCaddy()
: selectedIndex(0)
, screen(0)
{
}
void SetScreen(Screen* screen) { this->screen = screen; }
void Empty()
{
int index;
for (index = 0; index < (int)disks.size(); ++index)
{
disks[index].Close();
}
disks.clear();
selectedIndex = 0;
}
bool Insert(const FILINFO* fileInfo, bool readOnly);
DiskImage* GetCurrentDisk()
{
if (selectedIndex < disks.size())
return &disks[selectedIndex];
return 0;
}
DiskImage* NextDisk()
{
selectedIndex = (selectedIndex + 1) % (u32)disks.size();
return GetCurrentDisk();
}
DiskImage* PrevDisk()
{
selectedIndex = (selectedIndex - 1) % (u32)disks.size();
return GetCurrentDisk();
}
u32 GetNumberOfImages() const { return disks.size(); }
u32 GetSelectedIndex() const { return selectedIndex; }
DiskImage* GetImage(unsigned index) { return &disks[index]; }
DiskImage* SelectImage(unsigned index)
{
if (selectedIndex != index && index < disks.size())
{
selectedIndex = index;
return GetCurrentDisk();
}
return 0;
}
DiskImage* SelectFirstImage()
{
if (disks.size())
{
selectedIndex = 0;
return GetCurrentDisk();
}
return 0;
}
void Display();
bool Update();
private:
bool InsertD64(const FILINFO* fileInfo, unsigned char* diskImageData, unsigned size, bool readOnly);
bool InsertG64(const FILINFO* fileInfo, unsigned char* diskImageData, unsigned size, bool readOnly);
bool InsertNIB(const FILINFO* fileInfo, unsigned char* diskImageData, unsigned size, bool readOnly);
bool InsertNBZ(const FILINFO* fileInfo, unsigned char* diskImageData, unsigned size, bool readOnly);
void ShowSelectedImage(u32 index);
std::vector<DiskImage> disks;
u32 selectedIndex;
u32 oldCaddyIndex;
Screen* screen;
};
#endif

810
DiskImage.cpp Normal file
View File

@ -0,0 +1,810 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
// Pete Rittwage and Markus Brenner's code was heavly referenced and functions converted to CPP
// Used with Pete Rittwage's permission
#include "DiskImage.h"
#include "gcr.h"
#include "debug.h"
#include <string.h>
#include <ctype.h>
#include "lz.h"
extern "C"
{
#include "rpi-gpio.h"
}
unsigned char DiskImage::readBuffer[READBUFFER_SIZE];
static unsigned char compressionBuffer[HALF_TRACK_COUNT * NIB_TRACK_LENGTH];
static const unsigned short SECTOR_LENGTH = 256;
static const unsigned short SECTOR_LENGTH_WITH_CHECKSUM = 260;
static const unsigned char GCR_SYNC_BYTE = 0xff;
static const unsigned char GCR_GAP_BYTE = 0x55;
static const int SECTOR_HEADER_LENGTH = 8;
static const unsigned MAX_D64_SIZE = 0x30000;
#define NIB_HEADER_SIZE 0xFF
int gap_match_length = 7; // Used by gcr.cpp
const unsigned char DiskImage::SectorsPerTrack[42] =
{
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, // 1 - 17
19, 19, 19, 19, 19, 19, 19, // 18 - 24
18, 18, 18, 18, 18, 18, // 25 - 30
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, // 31 - 40
17, 17 // 41 - 42
// total 683-768 sectors
};
DiskImage::DiskImage()
: readOnly(false)
, dirty(false)
, attachedImageSize(0)
, fileInfo(0)
{
memset(tracks, 0x55, sizeof(tracks));
}
void DiskImage::Close()
{
switch (diskType)
{
case D64:
CloseD64();
break;
case G64:
CloseG64();
break;
case NIB:
CloseNIB();
break;
case NBZ:
CloseNBZ();
break;
default:
break;
}
memset(tracks, 0x55, sizeof(tracks));
memset(trackLengths, 0, sizeof(trackLengths));
diskType = NONE;
fileInfo = 0;
}
void DiskImage::DumpTrack(unsigned track)
{
unsigned char* src = tracks[track];
unsigned trackLength = trackLengths[track];
DEBUG_LOG("track = %d trackLength = %d\r\n", track, trackLength);
for (unsigned index = 0; index < trackLength; ++index)
{
DEBUG_LOG("%d %02x\r\n", index, src[index]);
}
}
bool DiskImage::OpenD64(const FILINFO* fileInfo, unsigned char* diskImage, unsigned size)
{
Close();
this->fileInfo = fileInfo;
unsigned offset = 0;
if (size > MAX_D64_SIZE)
size = MAX_D64_SIZE;
attachedImageSize = size;
for (unsigned halfTrackIndex = 0; halfTrackIndex < HALF_TRACK_COUNT; ++halfTrackIndex)
{
unsigned char track = (halfTrackIndex >> 1);
unsigned char* dest = tracks[halfTrackIndex];
trackLengths[halfTrackIndex] = SectorsPerTrack[track] * GCR_SECTOR_LENGTH;
if ((halfTrackIndex & 1) == 0)
{
if (offset < size) // This will allow for >35 tracks.
{
trackUsed[halfTrackIndex] = true;
//DEBUG_LOG("Track %d used\r\n", halfTrackIndex);
for (unsigned sectorNo = 0; sectorNo < SectorsPerTrack[track]; ++sectorNo)
{
convert_sector_to_GCR(diskImage + offset, dest, track + 1, sectorNo, diskImage + 0x165A2, 0);
dest += 361;
offset += SECTOR_LENGTH;
}
}
else
{
trackUsed[halfTrackIndex] = false;
//DEBUG_LOG("Track %d not used\r\n", halfTrackIndex);
}
}
else
{
trackUsed[halfTrackIndex] = false;
//DEBUG_LOG("Track %d not used\r\n", halfTrackIndex);
}
}
diskType = D64;
return true;
}
bool DiskImage::WriteD64()
{
if (readOnly)
return true;
FIL fp;
FRESULT res = f_open(&fp, fileInfo->fname, FA_CREATE_ALWAYS | FA_WRITE);
if (res == FR_OK)
{
u32 bytesToWrite;
u32 bytesWritten;
int track, sector;
BYTE id[3];
BYTE d64data[MAXBLOCKSONDISK * 256], *d64ptr;
int blocks_to_save = 0;
DEBUG_LOG("Writing D64 file...\r\n");
memset(d64data, 0, sizeof(d64data));
if (!GetID(34, id))
{
DEBUG_LOG("Cannot find directory sector.\r\n");
return false;
}
d64ptr = d64data;
for (track = 0; track <= 40 * 2; track += 2)
{
if (trackUsed[track])
{
//printf("Track %d\n", track);
for (sector = 0; sector < SectorsPerTrack[track / 2]; sector++)
{
ConvertSector(track, sector, d64ptr);
d64ptr += 256;
blocks_to_save++;
}
}
}
bytesToWrite = blocks_to_save * 256;
SetACTLed(true);
if (f_write(&fp, d64data, bytesToWrite, &bytesWritten) != FR_OK || bytesToWrite != bytesWritten)
{
SetACTLed(false);
DEBUG_LOG("Cannot write d64 data.\r\n");
f_close(&fp);
return false;
}
f_close(&fp);
f_utime(fileInfo->fname, fileInfo);
SetACTLed(false);
DEBUG_LOG("Converted %d blocks into D64 file\r\n", blocks_to_save);
return true;
}
else
{
DEBUG_LOG("Failed to open %s for write\r\n", fileInfo->fname);
return false;
}
}
void DiskImage::CloseD64()
{
if (dirty)
{
WriteD64();
dirty = false;
}
attachedImageSize = 0;
}
bool DiskImage::OpenG64(const FILINFO* fileInfo, unsigned char* diskImage, unsigned size)
{
Close();
this->fileInfo = fileInfo;
attachedImageSize = size;
if (memcmp(diskImage, "GCR-1541", 8) == 0)
{
//DEBUG_LOG("Is G64\r\n");
unsigned char numTracks = diskImage[9];
//DEBUG_LOG("numTracks = %d\r\n", numTracks);
unsigned char* data = diskImage + 12;
unsigned char* speedZoneData = diskImage + 0x15c;
unsigned short trackLength = 0;
unsigned track;
for (track = 0; track < numTracks; ++track)
{
unsigned offset = *(unsigned*)data;
data += 4;
//DEBUG_LOG("Track = %d Offset = %x\r\n", track, offset);
trackDensity[track] = *(unsigned*)(speedZoneData + track * 4);
if (offset == 0)
{
trackLengths[track] = capacity_max[trackDensity[track]];
trackUsed[track] = false;
}
else
{
unsigned char* trackData = diskImage + offset;
trackLength = *(unsigned short*)(trackData);
//DEBUG_LOG("trackLength = %d offset = %d\r\n", trackLength, offset);
trackData += 2;
trackLengths[track] = trackLength;
memcpy(tracks[track], trackData, trackLength);
trackUsed[track] = true;
//DEBUG_LOG("%d has data\r\n", track);
}
}
diskType = G64;
return true;
}
return false;
}
static bool WriteDwords(FIL* fp, u32* values, u32 amount)
{
u32 index;
u32 bytesToWrite = 4;
u32 bytesWritten;
for (index = 0; index < amount; ++index)
{
if (f_write(fp, &values[index], bytesToWrite, &bytesWritten) != FR_OK || bytesToWrite != bytesWritten)
return false;
}
return true;
}
bool DiskImage::WriteG64()
{
if (readOnly)
return true;
FIL fp;
FRESULT res = f_open(&fp, fileInfo->fname, FA_CREATE_ALWAYS | FA_WRITE);
if (res == FR_OK)
{
u32 bytesToWrite;
u32 bytesWritten;
int track_inc = 1;
BYTE header[12];
DWORD gcr_track_p[MAX_HALFTRACKS_1541] = { 0 };
DWORD gcr_speed_p[MAX_HALFTRACKS_1541] = { 0 };
BYTE gcr_track[NIB_TRACK_LENGTH + 2];
size_t track_len;
int index = 0, track;
BYTE buffer[NIB_TRACK_LENGTH], tempfillbyte;
DEBUG_LOG("Writing G64 file...\r\n");
//DEBUG_LOG("G64 Track Length = %d", G64_TRACK_MAXLEN);
strcpy((char *)header, "GCR-1541");
header[8] = 0;
header[9] = MAX_HALFTRACKS_1541;
header[10] = (BYTE)(G64_TRACK_MAXLEN % 256);
header[11] = (BYTE)(G64_TRACK_MAXLEN / 256);
bytesToWrite = sizeof(header);
SetACTLed(true);
if (f_write(&fp, header, bytesToWrite, &bytesWritten) != FR_OK || bytesToWrite != bytesWritten)
{
SetACTLed(false);
DEBUG_LOG("Cannot write G64 header.\r\n");
f_close(&fp);
return false;
}
SetACTLed(false);
for (track = 0; track < MAX_HALFTRACKS_1541; track += track_inc)
{
if (trackLengths[track] == 0 || !trackUsed[track])
{
gcr_track_p[track] = 0;
gcr_speed_p[track] = 0;
}
else
{
gcr_track_p[track] = 0xc + (MAX_TRACKS_1541 * 16) + (index++ * (G64_TRACK_MAXLEN + 2));
gcr_speed_p[track] = trackDensity[track] & 3;
}
}
SetACTLed(true);
WriteDwords(&fp, (u32*)gcr_track_p, MAX_HALFTRACKS_1541);
WriteDwords(&fp, (u32*)gcr_speed_p, MAX_HALFTRACKS_1541);
SetACTLed(false);
for (track = 0; track < MAX_HALFTRACKS_1541; track += track_inc)
{
track_len = trackLengths[track];
if (track_len>G64_TRACK_MAXLEN) track_len = G64_TRACK_MAXLEN;
if (!track_len || !trackUsed[track]) continue;
tempfillbyte = 0x55;
memset(&gcr_track[2], tempfillbyte, G64_TRACK_MAXLEN);
gcr_track[0] = (BYTE)(track_len % 256);
gcr_track[1] = (BYTE)(track_len / 256);
memcpy(buffer, tracks[track], track_len);
memcpy(gcr_track + 2, buffer, track_len);
bytesToWrite = G64_TRACK_MAXLEN + 2;
SetACTLed(true);
if (f_write(&fp, gcr_track, bytesToWrite, &bytesWritten) != FR_OK || bytesToWrite != bytesWritten)
{
SetACTLed(false);
DEBUG_LOG("Cannot write track data.\r\n");
f_close(&fp);
return false;
}
SetACTLed(false);
}
f_close(&fp);
DEBUG_LOG("nSuccessfully saved G64\r\n");
return true;
}
else
{
DEBUG_LOG("Failed to open %s for write\r\n", fileInfo->fname);
return false;
}
}
void DiskImage::CloseG64()
{
if (dirty)
{
WriteG64();
dirty = false;
}
attachedImageSize = 0;
}
bool DiskImage::OpenNIB(const FILINFO* fileInfo, unsigned char* diskImage, unsigned size)
{
int track, t_index = 0, h_index = 0;
Close();
this->fileInfo = fileInfo;
attachedImageSize = size;
if (memcmp(diskImage, "MNIB-1541-RAW", 13) == 0)
{
for (track = 0; track < (MAX_TRACKS_1541 * 2); ++track)
{
trackLengths[track] = capacity_max[trackDensity[track]];
trackUsed[track] = false;
}
while (diskImage[0x10 + h_index])
{
track = diskImage[0x10 + h_index] - 2;
unsigned char v = diskImage[0x11 + h_index];
trackDensity[track] = (v & 0x03);
DEBUG_LOG("Converting NIB track %d (%d.%d)\r\n", track, track >> 1, track & 1 ? 5 : 0);
unsigned char* nibdata = diskImage + (t_index * NIB_TRACK_LENGTH) + 0x100;
int align;
trackLengths[track] = extract_GCR_track(tracks[track], nibdata, &align
//, ALIGN_GAP
, ALIGN_NONE
, capacity_min[trackDensity[track]],
capacity_max[trackDensity[track]]);
trackUsed[track] = true;
h_index += 2;
t_index++;
}
DEBUG_LOG("Successfully parsed NIB data for %d tracks\n", t_index);
diskType = NIB;
return true;
}
return false;
}
bool DiskImage::WriteNIB()
{
if (readOnly)
return true;
FIL fp;
FRESULT res = f_open(&fp, fileInfo->fname, FA_CREATE_ALWAYS | FA_WRITE);
if (res == FR_OK)
{
u32 bytesToWrite;
u32 bytesWritten;
int track;
char header[0x100];
int header_entry = 0;
DEBUG_LOG("Converting to NIB format...\n");
memset(header, 0, sizeof(header));
sprintf(header, "MNIB-1541-RAW%c%c%c", 1, 0, 0);
for (track = 0; track < (MAX_TRACKS_1541 * 2); ++track)
{
if (trackUsed[track])
{
header[0x10 + (header_entry * 2)] = (BYTE)track + 2;
header[0x10 + (header_entry * 2) + 1] = trackDensity[track];
header_entry++;
}
}
bytesToWrite = sizeof(header);
SetACTLed(true);
if (f_write(&fp, header, bytesToWrite, &bytesWritten) != FR_OK || bytesToWrite != bytesWritten)
{
DEBUG_LOG("Cannot write track data.\r\n");
}
else
{
bytesToWrite = NIB_TRACK_LENGTH;
for (track = 0; track < (MAX_TRACKS_1541 * 2); ++track)
{
if (trackUsed[track])
{
if (f_write(&fp, tracks[track], bytesToWrite, &bytesWritten) != FR_OK || bytesToWrite != bytesWritten)
{
DEBUG_LOG("Cannot write track data.\r\n");
}
}
}
}
SetACTLed(false);
f_close(&fp);
DEBUG_LOG("nSuccessfully saved NIB\r\n");
return true;
}
else
{
DEBUG_LOG("Failed to open %s for write\r\n", fileInfo->fname);
return false;
}
}
void DiskImage::CloseNIB()
{
if (dirty)
{
WriteNIB();
dirty = false;
}
attachedImageSize = 0;
}
bool DiskImage::OpenNBZ(const FILINFO* fileInfo, unsigned char* diskImage, unsigned size)
{
Close();
if ((size = LZ_Uncompress(diskImage, compressionBuffer, size)))
{
if (OpenNIB(fileInfo, compressionBuffer, size))
{
diskType = NIB;
return true;
}
}
return false;
}
bool DiskImage::WriteNBZ()
{
bool success = false;
if (readOnly)
return true;
SetACTLed(true);
if (WriteNIB())
{
FIL fp;
FRESULT res = f_open(&fp, fileInfo->fname, FA_READ);
if (res == FR_OK)
{
u32 bytesRead;
f_read(&fp, readBuffer, READBUFFER_SIZE, &bytesRead);
f_close(&fp);
DEBUG_LOG("Reloaded %s - %d for compression\r\n", fileInfo->fname, bytesRead);
bytesRead = LZ_Compress(readBuffer, compressionBuffer, bytesRead);
if (bytesRead)
{
res = f_open(&fp, fileInfo->fname, FA_CREATE_ALWAYS | FA_WRITE);
if (res == FR_OK)
{
u32 bytesToWrite = bytesRead;
u32 bytesWritten;
if (f_write(&fp, compressionBuffer, bytesToWrite, &bytesWritten) != FR_OK || bytesToWrite != bytesWritten)
{
DEBUG_LOG("Cannot write NBZ data.\r\n");
}
else
{
success = true;
}
f_close(&fp);
}
}
}
}
SetACTLed(false);
return success;
}
void DiskImage::CloseNBZ()
{
if (dirty)
{
WriteNBZ();
dirty = false;
}
attachedImageSize = 0;
}
bool DiskImage::GetDecodedSector(u32 track, u32 sector, u8* buffer)
{
if (track > 0)
{
track = (track - 1) * 2;
if (trackUsed[track])
return ConvertSector(track, sector, buffer);
}
return false;
}
DiskImage::DiskType DiskImage::GetDiskImageTypeViaExtention(const char* diskImageName)
{
char* ext = strrchr((char*)diskImageName, '.');
if (ext)
{
if (toupper((char)ext[1]) == 'G' && ext[2] == '6' && ext[3] == '4')
return G64;
else if (toupper((char)ext[1]) == 'N' && toupper((char)ext[2]) == 'I' && toupper((char)ext[3]) == 'B')
return NIB;
else if (toupper((char)ext[1]) == 'N' && toupper((char)ext[2]) == 'B' && toupper((char)ext[3]) == 'Z')
return NBZ;
else if (toupper((char)ext[1]) == 'D' && ext[2] == '6' && ext[3] == '4')
return D64;
else if (toupper((char)ext[1]) == 'L' && toupper((char)ext[2]) == 'S' && toupper((char)ext[3]) == 'T')
return LST;
}
return NONE;
}
bool DiskImage::IsDiskImageExtention(const char* diskImageName)
{
return GetDiskImageTypeViaExtention(diskImageName) != NONE;
}
bool DiskImage::IsLSTExtention(const char* diskImageName)
{
char* ext = strrchr((char*)diskImageName, '.');
if (ext && toupper((char)ext[1]) == 'L' && toupper((char)ext[2]) == 'S' && toupper((char)ext[3]) == 'T')
return true;
return false;
}
bool DiskImage::ConvertSector(unsigned track, unsigned sector, unsigned char* data)
{
unsigned char buffer[SECTOR_LENGTH_WITH_CHECKSUM];
unsigned char checkSum;
int index;
int bitIndex;
bitIndex = FindSectorHeader(track, sector, 0);
if (bitIndex < 0)
return false;
bitIndex = FindSync(track, bitIndex, (SECTOR_LENGTH_WITH_CHECKSUM * 2) * 8);
if (bitIndex < 0)
return false;
DecodeBlock(track, bitIndex, buffer, SECTOR_LENGTH_WITH_CHECKSUM / 4);
checkSum = buffer[257];
for (index = 0; index < SECTOR_LENGTH; ++index)
{
data[index] = buffer[index + 1];
checkSum ^= data[index];
}
if (buffer[0] != 0x07)
return false; // No data block
return checkSum == 0;
}
void DiskImage::DecodeBlock(unsigned track, int bitIndex, unsigned char* buf, int num)
{
int shift, i, j;
unsigned char gcr[5];
unsigned char byte;
unsigned char* offset;
unsigned char* end = tracks[track] + trackLengths[track];
shift = bitIndex & 7;
offset = tracks[track] + (bitIndex >> 3);
byte = offset[0] << shift;
for (i = 0; i < num; i++, buf += 4)
{
for (j = 0; j < 5; j++)
{
offset++;
if (offset >= end)
offset = tracks[track];
if (shift)
{
gcr[j] = byte | ((offset[0] << shift) >> 8);
byte = offset[0] << shift;
}
else
{
gcr[j] = byte;
byte = offset[0];
}
}
convert_4bytes_from_GCR(gcr, buf);
}
}
int DiskImage::FindSync(unsigned track, int bitIndex, int maxBits, int* syncStartIndex)
{
int readShiftRegister = 0;
unsigned char byte = tracks[track][bitIndex >> 3] << (bitIndex & 7);
bool prevBitZero = true;
while (maxBits--)
{
if (byte & 0x80)
{
if (syncStartIndex && prevBitZero)
*syncStartIndex = bitIndex;
prevBitZero = false;
readShiftRegister = (readShiftRegister << 1) | 1;
}
else
{
prevBitZero = true;
if (~readShiftRegister & 0x3ff)
readShiftRegister <<= 1;
else
return bitIndex;
}
if (~bitIndex & 7)
{
bitIndex++;
byte <<= 1;
}
else
{
bitIndex++;
if (bitIndex >= NIB_TRACK_LENGTH * 8)
bitIndex = 0;
byte = tracks[track][bitIndex >> 3];
}
}
return -1;
}
int DiskImage::FindSectorHeader(unsigned track, unsigned sector, unsigned char* id)
{
unsigned char header[10];
int bitIndex;
int bitIndexPrev;
bitIndex = 0;
bitIndexPrev = -1;
for (;;)
{
bitIndex = FindSync(track, bitIndex, NIB_TRACK_LENGTH * 8);
if (bitIndexPrev == bitIndex)
break;
if (bitIndexPrev < 0)
bitIndexPrev = bitIndex;
DecodeBlock(track, bitIndex, header, 2);
if (header[0] == 0x08 && header[2] == sector)
{
if (id)
{
id[0] = header[5];
id[1] = header[4];
}
return bitIndex;
}
}
return -1;
}
unsigned DiskImage::GetID(unsigned track, unsigned char* id)
{
if (FindSectorHeader(track, 0, id) >= 0)
return 1;
return 0;
}
unsigned DiskImage::LastTrackUsed()
{
unsigned i;
unsigned lastTrackUsed = 0;
for (i = 0; i < HALF_TRACK_COUNT; ++i)
{
if (trackUsed[i])
lastTrackUsed = i;
}
return lastTrackUsed;
}

158
DiskImage.h Normal file
View File

@ -0,0 +1,158 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#ifndef DISKIMAGE_H
#define DISKIMAGE_H
#include "types.h"
#include "ff.h"
#define READBUFFER_SIZE 1024 * 512
#define NIB_TRACK_LENGTH 0x2000
#define BAM_OFFSET 4
#define BAM_ENTRY_SIZE 4
#define DIR_ENTRY_OFFSET_TYPE 0
#define DIR_ENTRY_OFFSET_NAME 3
#define DIR_ENTRY_OFFSET_BLOCKS 28
#define DIR_ENTRY_NAME_LENGTH 18-2
static const unsigned char HALF_TRACK_COUNT = 84;
static const unsigned short GCR_SYNC_LENGTH = 5;
static const unsigned short GCR_HEADER_LENGTH = 10;
static const unsigned short GCR_HEADER_GAP_LENGTH = 8;
static const unsigned short GCR_SECTOR_DATA_LENGTH = 325;
static const unsigned short GCR_SECTOR_GAP_LENGTH = 8;
static const unsigned short GCR_SECTOR_LENGTH = GCR_SYNC_LENGTH + GCR_HEADER_LENGTH + GCR_HEADER_GAP_LENGTH + GCR_SYNC_LENGTH + GCR_SECTOR_DATA_LENGTH + GCR_SECTOR_GAP_LENGTH; //361
static const unsigned short G64_MAX_TRACK_LENGTH = 7928;
class DiskImage
{
public:
enum DiskType
{
NONE,
D64,
G64,
NIB,
NBZ,
LST,
RAW
};
DiskImage();
bool OpenD64(const FILINFO* fileInfo, unsigned char* diskImage, unsigned size);
bool OpenG64(const FILINFO* fileInfo, unsigned char* diskImage, unsigned size);
bool OpenNIB(const FILINFO* fileInfo, unsigned char* diskImage, unsigned size);
bool OpenNBZ(const FILINFO* fileInfo, unsigned char* diskImage, unsigned size);
void Close();
bool GetDecodedSector(u32 track, u32 sector, u8* buffer);
inline bool GetNextBit(u32 track, u32 byte, u32 bit)
{
if (attachedImageSize == 0)
return 0;
return ((tracks[track][byte] >> bit) & 1) != 0;
}
inline void SetBit(u32 track, u32 byte, u32 bit, bool value)
{
if (attachedImageSize == 0)
return;
u8 dataOld = tracks[track][byte];
u8 bitMask = 1 << bit;
if (value)
{
TestDirty(track, (dataOld & bitMask) == 0);
tracks[track][byte] |= bitMask;
}
else
{
TestDirty(track, (dataOld & bitMask) != 0);
tracks[track][byte] &= ~bitMask;
}
}
static const unsigned char SectorsPerTrack[42];
void DumpTrack(unsigned track);
const char* GetName() { return fileInfo->fname; }
inline unsigned BitsInTrack(unsigned track) const { return trackLengths[track] << 3; }
static DiskType GetDiskImageTypeViaExtention(const char* diskImageName);
static bool IsDiskImageExtention(const char* diskImageName);
static bool IsLSTExtention(const char* diskImageName);
bool GetReadOnly() const { return readOnly; }
void SetReadOnly(bool readOnly) { this->readOnly = readOnly; }
unsigned LastTrackUsed();
static unsigned char readBuffer[READBUFFER_SIZE];
private:
void CloseD64();
void CloseG64();
void CloseNIB();
void CloseNBZ();
bool WriteD64();
bool WriteG64();
bool WriteNIB();
bool WriteNBZ();
inline void TestDirty(u32 track, bool isDirty)
{
if (isDirty)
{
trackDirty[track] = true;
trackUsed[track] = true;
dirty = true;
}
}
bool ConvertSector(unsigned track, unsigned sector, unsigned char* buffer);
void DecodeBlock(unsigned track, int bitIndex, unsigned char* buf, int num);
unsigned GetID(unsigned track, unsigned char* id);
int FindSectorHeader(unsigned track, unsigned sector, unsigned char* id);
int FindSync(unsigned track, int bitIndex, int maxBits, int* syncStartIndex = 0);
bool readOnly;
bool dirty;
unsigned attachedImageSize;
DiskType diskType;
const FILINFO* fileInfo;
unsigned char tracks[HALF_TRACK_COUNT][NIB_TRACK_LENGTH];
unsigned short trackLengths[HALF_TRACK_COUNT];
unsigned char trackDensity[HALF_TRACK_COUNT];
bool trackDirty[HALF_TRACK_COUNT];
bool trackUsed[HALF_TRACK_COUNT];
};
#endif

530
Drive.cpp Normal file
View File

@ -0,0 +1,530 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#include "Drive.h"
#include "m6522.h"
#include "debug.h"
// There is a lot going on even though the emulation code is extremely small.
// A few counters, shift registers and the occasional logic gate takes a surprisingly small amount of code to implement.
// Note: A 1541 is an upgraded (cost reduced) version of the 1540.
// In a 1541 a lot of the drive logic is implemented in a custom asic chip 235572. Internally, this chip contains essentially the same logic that was used in the 1540.
// Commodore chose to reduce the chip count to also reduce power consumption, heat, and production costs.
// Schematics for the 1540 drive logic made this emulator possible. The 1540 logic was emulated hence chip names reference the 1540.
// I have a VIC-1541 drive with a 1540 type board in it. The chips are laid out in columns A through to H.
// Considering how prolific magnetic storage has been for many decades there is surprisingly little information (retaliative to other computer hardware like CPUs) into how disk drives actually work.
// This is how I understand it (and I apologise for the verbosity but it helped me come to that understanding).
//
// Magnetic media can provide simple, inexpensive and reliable storage medium.
// Reliability being a challenge considering that disks can vary between different disk manufactures and brands.
// Physical properties of drives, like alignment and rotation speed can vary slightly from drive to drive. These properties can even vary overtime or due to temperature fluctuations within the same drive.
// Digital information needs to be stored via an analogue system that can tolerate minor inconsistencies between theses physical properties.
//
// A magnetic field has two important characteristics flux density, and polarity (North and South Poles).
// At first glance, the two magnetic polarities, N and S, look ideal to nicely to represent a 1 or 0.
// Hall effect sensors can detect flux density of a magnetic field (even a constant one) but generally require stronger flux densities than found on the coating of floppy media can provide, so are not used to read data.
// And unfortunately, hall effect sensors have no way of writing data only reading it.
//
// MaxwellFaraday law/equation states that a time varying magnetic field will induce a time varing electric field and visa versa.
// Disks drive heads use this principal, and can therefore, read and write data.
// Yet they can only detect/read and create/write changes in the disk's analogue magnetic field over time.
// The larger and quicker the change the easier it is to detect and read.
// Writing works in reverse where a changing electric signal creates a changing magnetic field.
//
// So how are digital 1s and 0s represented using this analogue magnetic field?
// A 1 is represented by the changing analogue magnetic field changing from N to S and visa versa.
// But what about the 0s? A 0 is represented by how quickly the magnetic field changes between any two 1 bits.
// So technically only the 1s are written to the disk. 0s are infered from the rate at which the magnetic field changes between two 1 bits.
// The slower the analogue change between N-S/S-N change the more 0s there will be in between the 1s.
// Long groups of consecutive 0s will eventually slow the rate of change down to a point where the magnetic field becomes almost constant.
// A constant magnetic field will stop induction.
// Due to the way the amplification circuits operate (explained below), an almost constant magnetic field can become problematic producing false readings.
//
// To solve the problem of preventing long groups of 0s and to keep the magnetic field varing over time various data encoding schemes were created.
// These data encoding schemes sacrifice disk density for maintaining a rapidly changing, easy to read magnetic field.
// Originally, up to 50% of a disk's density was sacrificed for software encoding schemes. Interleaving clock bits with data bits.
// Then some bright sparks invented other more efficient schemes, almost doubling the density of the of the disk (as space for the clock bits could now be used for data).
// Some marketing genius picked up on the words “double density” and the name stuck, even though the disk was always capable of storing the exact same number of bits!
// A encoding scheme commonly used at the time of the 1541 is called GCR.
// GCR encodes 4 bits into 5 where any combination cannot produce no more than two 0s in a row. This sacrifices only 20% of the disk's density.
// There is no drive hardware that dictates that any specific encoding scheme nees to be used.
// Software is free to do whatever it wants, even change the encoding schemes used over different parts of the disk or even within a single track.
// But any coding scheme that allows long groups of 0s will experience side effects.
// Side effects of long groups of 0s can cause a random 1 to be read incorrectly (explained below).
// Many copy protection schemes rely on these side effects occurring.
// The decoder in the 1541 is also hard coded to output a 1 after every three 0s even if there is no magnetic field change (flux reversal). So a 1541 cannot read more than three 0s in a row.
//
// A read/write head is a coil with a centre tap effectively creating two coils. One coil detects/creates magnetic field changes in one direction (N-S) and the other coil in the other direction (S-N).
// There is also a third coil use to erase.
// The difference between the electric signals in each of the two coils is amplified.
// Remember that we are only interested in the rate in which the magnetic field is changing not its relative amplitude.
// Drives vary slightly and even the same drive can vary with temperature.
// Because of this and to increase alignment tolerance the relative amplitude of the magnetic field can be ignored. The rate of change is important.
// The amplifier therefore amplifies the signal with a varying gain. This is called 'Automatic Gain Control'.
// It is always trying to amplify the signal as much as it needs. For a strong signal not as much as for a weak signal.
//
// The signal is then filtered. A low-pass filter attenuates noise and unwanted harmonics.
//
// In a 1540;-
// This is done by L8-L11, and C16.
//
// To detect the all important change in magnetic field direction (ie a flux reversal) the signal is again amplified.
// But this time the amplifier has been configured as a differentiator as well.
// A differentiator amplifier produces an output signal that is the first derivative of the input signal.
// That is, the output signal is directly proportional to the rate of change of the input signal.
// This means that every place in the input signal where there is a peak or trough (ie the signal changes direction, namely, a flux reversal) the new signal will cross the zero point (in the analogue waveform).
//
// The differentiated signal can now be analysed. A zero crossing represents a 1. The absence of a zero crossing over a certain time threshold represents a 0.
// The problem then becomes how to detect zero crossings and time them.
// Detecting zero crossings is solved using another operational amplifier configured as a comparator with a reference voltage of zero.
// The comparator output toggles each time its inputs sense a zero crossing. The signal now being analysed is a simple square wave.
//
// In a 1540;-
// Heads read 7mv.
// First amp UH7 amplifies this to 350mv.
// Second amp UH5 amplifies this and differentiates it to 3.5v.
// Comparator UH4 and the pulse generator output 5v TTL.
//
// The comparator output is then applied to the time domain filter.
// The primary purpose of the time domain filter is to generate an output pulse for each transition of the comparator output.
// These pulses correspond to valid 1s being read off the disk and the absence of pulses the infered 0s.
// The secondary purpose is to filter out false zero crossings which can be caused by noise aggravated by media defect and contamination.
// Filtering out false information involves the use of an "ignore window", during which time any zero crossings are ignored.
//
// The time domain filter consists of a two one shots, and a D type flip flop.
// Each transition of the comparator output triggers the first one shot, which provides the ignore window.
// When the output of the first one shot returns to the normal level, the D type flip flop clocks through the new data (from the comparator), causing the Q and !Q outputs to switch.
// This in turn triggers the second one shot, to produce an output pulse.
// However, the second one shot will only be triggered if the comparator output has changed since the last clocking of the D type flip flop, and remained at that new level.
// If the comparator changed level due to noise on a slowly changing magnetic field and returned to its original level within the first one shot's period, the D type flip flop outputs will not change, and the second one shot will not generate a new output pulse.
//
// Thus, the zero crossing detector and the pulse generator result in pulses occurring at the original input signal's peak points (ie flux reversals).
//
// So in a 1540;-
// The output of the comparator is applied to the valid pulse detector(UG2D, UG3A, and UF6A).
// UG2D, along with C27, R24, and R25, forms an edge detector.
// Pin 11 of UG2D will produce a 500 nanosecond active high pulse on rising edges of the comparator's output and will produce a 150 nanosecond high pulse on falling edges of the comparator's output.
// The pulse out of UG2D is applied to UG3A, a single shot multivibrator.
// UG3A produces an active low pulse which is approximately 2.5 microseconds wide.
// Flip flop UF6A is clocked on the trailing edge of the output pulse of UG3A.
// The output of the comparator must be maintained for at least 2.5 microseconds in order to be latched into the flip flop.
// If a narrow noise pulse triggers this circuit, the output of the flip flop will not change since the noise pulse will terminate before UG3A triggers UF6A.
// The output of the flip flop (UF6A) will reflect the data delayed by approximately 2.5 microseconds.
// The valid data out of the valid pulse detector is applied to an edge detector consisting of UG2A and UG3B.
// UG2A operates the same as UG2D above.
// The pulses out of UG2A trigger UG3B, a single shot multivibrator.
// UG3B produces a narrow pulse, which represents transitions of the data.
// This output is applied to the timing circuit to synchronise the encoder / decoder clock
//
//
// The problem of zero crossing (hence flux reversals) is solved, now 1s can be detetected. Detecting 0s now becomes a task of timing the gap between the 1s.
//
// After all this explanation, NONE of this needs to be emulated!
//
// Up to this point the hardware explained so far is responsible for converting the analogue siganls into clean digital signals.
// Emulated disk images already consist of digital information and hence the hardware explained so far has to some degree already been emulated.
// What does need to be emulated though (at least simulated) is the side effects the hardware can produce.
//
//
// Side effects
// When software uses a valid encoding scheme long runs of 0 are avoided.
// Even if the disk is old or the drive is slightly miss alligned or varying in rotation speed (within a certain range) there should not be a problem reading the data.
// But there is still nothing that to prevent the magnetic field on a disk changing slowly.
// So what happens when the magnetic field is changing slowly but just fast enough for induction to be detected?
// The first amplifier will detect the weak induced signal and maximise its gain. With a maximised gain noise can become a problem.
// The slope of the slowly changing signal will be differentiated to produce a signal that will be near zero.
// A low amplitude signal varying near zero is fine and valid but a low amplitude signal varying around zero is bad as each zero crossing will be detected incorrectly as a 1.
// So any noise on the differentiated signal could cause it to cross the zero point causing the comparator to switch.
// The time domain filter will normally filter most of the high frequency false crossings out.
// But if these random zero crossing just happen to occur at the normally expected rate of valid flux reversals, random 1s, along with the groups of 0s, will be read.
// On a signal that is near zero these random noise frequencies can and do occur at the normally valid ranges.
// Copy protection schemes rely on this and deliberately place a slow changing magnetic field on the disk.
// The software then reads that section of the disk multiple times. Each time comparing the values, looking for the random 1s.
// If the 1s are consistent then it knows that the field on the disk is no longer the slowly changing one of the original but that of a faster changing one of a copy.
// This side effect is simulated by the code below.
//
// Now all that is left to solve is the problem of timing.
// The data on the disk is divided into cells. If a transition occurs at the beginning of a cell, the cell is interpreted as a logic 1. If no transition occurs, the cell is interpreted as a logic 0.
// We just need to time the cell. The cell handling is the responsibility of the encoder/decoder (for writing/reading).
//
// For the standard CBM format more information (hence more cells) are stored on the outside of a disk than the inside.
// This variation keeps the bit density relatively constant over the surface of the disk.
// The disk spins at a near to constant rate.
// So for a round disk there is more surface area on the outside tracks than the inside.
// The timing of the cells and hence the encoder/decoder's clock can vary upto four levels (set via 2 pins in the VIA($1C00) PB5 and PB6).
// The encoder/decoder is usually clocked at a fester rate when on the outer tracks than when on the inner tracks.
// It is the encoder/decoder clock which determines how many bits will fit on any one track.
//
// The core clock generator inside a 1541 is a 16Mz crystal. All timings including the encoder/decoder clock is derived from this.
//
// The encoder/decoder clock is simply a counter. UE7 a 74LS193 Synchronous 4-Bit Up/Down Counter.
// If counting at 16Mz it will take 16 cycles to count to 16 and therefore overflow/trigger a carry every 1us (ie 1MHz)
// It always counts at the same rate but can be made to trigger quicker if it is preloaded with an initial count each time it begins a new count.
// This preloading value comes from the CLOCK_SEL_AB pins of the VIA's PB5 and PB6 (set via the CPU) and can therefore be preloaded with a value of 0 to 3.
// The programmable divider is controlled by the CLOCK SEL A and CLOCK SEL B lines and selects the density level.
// Density/Bit Rate Index 3 2 1 0
// CLOCK SEL A 1 0 1 0
// CLOCK SEL B 1 1 0 0
// 16MHz Division Factor 13 14 15 16
// Encoder / Decoder Clock Freq (MHz) 1.2307 1.1428 1.0666 1.00
// Sectors per Track 21 20 18 17
// Track Numbers 1 - 17 18 - 24 25 - 30 31 - 42
// Division Factor/16 0.8125 0.875 0.9375 1
// Bit cell length in us (MAX) 3.25 3.5 3.75 4
// (4xDivision Factor/16)
//
//
// Nothing in the hardware limits what timings are required for any section of the disk. Software is free to change the timing at any time for any section of the disk (even within the same track).
//
// The encoder/decoder itself is made up of a four bit counter (UE4) and a NOR gate (UE5A).
// The counter is configured to always count up, clocked by the encoder/decoder clock.
// Any flux reversal either true or induced by noise will reset the encoder/decoder's counter (UE4) back to zero and encoder/decoder clock's counter (UE7) back to its preload density values.
// The encoder/decoder counter has 4 outputs A, B, C and D. They represent the bit value of the UE4's current count where A is the least significant bit and D the most.
// The B output forms the serial data clock.
// The C and D are NORed together and the output forms the serial data.
// The serial data is fed into the Read Shift Register (UD2) where it is converted from serial to parallel and can be gated onto the VIA's portA via the read latch UC3.
//
// A bit cell is four encoder/decoder clock pulses wide, as the 2nd bit (output B) in UF4's count controls the serial clock. Based on the VIA's density preload values a cell is at a maximum 3.25-4us long (shorter if it contains a flux reversal).
// If a flux reversal occurs at the beginning of a cell, that cell is a 1 else that cell is a 0.
// If a flux reversal occurs, UF4's counter is cleared and the timing circuit is reset to start the encoder/decoder clock at the beginning of the VIA's current density setting.
// To phase lock onto data we must always begin with a 1 hence the need for a soft SYNC marker full of 1s.
//
// When B becomes high the NORed value of C and D (serial data) is sent to the shift register.
// If there is a flux reversal the counter resets. Two counts later a one is always shifted in.
// If after the first flux reversal there is an absence of others, UE4 continues to count up and each time B goes high (counts 6 10 & 14) only 0s will be shifted in (at the rate dictated by the encoder/decoders's clock and its density preload setting).
// If UE4's count is left to cycle (ie no flux reversals for 16 counts) a 1 will be shifted in regardless.
// This limits the hardware to only read three consecutive 0s between any two 1s. (with GCR this will never occur)
//
// The 1541 uses soft sectors. The hardware can only detect the sync sequence of then one bits in a row and everything must then be phase locked to that.
// The sync sequence phase locks to blocks. Blocks can really be any size, even the entire track.
// UC2 monitors the parallel output of UD2/UE4, when all 10 bits are 1, the output pin 9 goes low indicating a SYNC sequence has been read.
// Whenever the SYNC is asserted UE3 is reset and we are also phase locked to bytes.
// UE3 continues the phase lock to bytes as it is another counter responsible for counting 8 serial clocks.
// When 8 bits have been counted, UF3 pin 12 goes low, UC1 pin 10 goes high, and UF3 pin 8 goes low indicating a byte is ready to be read by the CPU.
//
// And as we have already seen the flux reversals phase lock to bits.
//
// So now the analogue signal can be phase locked onto and the data read/written in a bit stream, despite minor physical inconsistencies between disks and drives.
// The Encoder/Decoder Clock
// UE7 74LS193 Synchronous 4-Bit Up/Down Counter
// - clocks UF4
// - counts up on pin 5 and this is connected to 16Mhz clock
// - never counts down as pin 4 is tied to VCC
// - inputs A and B are connected to VIA's PB5/6 CLOCK_SEL_AB input C and D to GND
// - pin 12 !CO (!carry out) is output to UE5C (NOR gate used as an inverter) and the inverted signal is called "Encoder/Decoder Clock"
// - carry out is the only output used.
// - pin 14 CLR is grounded so the count is never cleared this way
// - pin 11 LOAD is NORed (UE5D) with our inverted !CO (pin 12) and "Bit Sync" signal. "Bit Sync" is data from the read amplifier, differentiator, compatitor, time domain filter and pulse generator.
// - the counter resets to VIA's current PB5/6 CLOCK_SEL_AB
//
// The Encoder/Decoder
// UF4 74LS193 Synchronous 4-Bit Up/Down Counter (used to provide clocks for the UD2 serial to parallel shifter and the UE3 bit counter)
// - counts up on pin 5 and this is connected to the inverted UE7's carry out ie "Encoder/Decoder Clock"
// - never counts down as pin 4 is tied to VCC
// - pin 11 !LOAD is tied to VCC so the count will never be altered this way
// - pin 14 CLR receives data from the read amplifiers (ie "Bit Sync")
// - so a 1 will reset the count
// - outputs D and C are connected to a NOR gate UE5A
// - output A
// - output B ie every 2nd count
// drives UD2 shifter
//
// Byte Phase Lock
// UE3 74LS191 Presettable 4-Bit Up/Down Counter
// - pin 14 CLOCK clocked by
// - when UF4 counts 2, 6, 10 and 14 (2 is the only count that outputs a 1 into readShiftRegister)
// - Will count 8 bits everytime UF4 cycles twice
// - pins 4 (!ENABLE) and 5 (DOWN/UP) connected to GND
// - puts it in count up mode constantly
// - pin 7 (OUTPUT D) is NC
// - eventhough it can count to 16 the high bit is ignored and system treats it as a don't care state
// - low 3 bits only makes it a 3 bit counter that has 8 counts
// - pin 11 !LOAD
// - this is connected to the output of UC2 74ls133 a 13 input NAND gate
// - 2 inputs are connected to VCC
// - 8 inputs are connected to
// - 2 inputs are connected to
// - 1 input is coneeced to read/write signal
// - UC2 output can only asserted when reading
// - the output of UC2 is then called !BLOCK SYNC
// - pins 15, 1 and 10 inputs A,B and C are connected to GND. Pin 0 input D NC and as a don't care state
// - so, resets back to zero when !LOAD is asserted
// - ie when in SYNC will always reset until the first 0 bit
// - pins 3, 2 and 6 outputs A, B and C (pin 7 output D is NC)
// - NANDed together by UF3A and this is inverted by UC1E 74ls06
// - this then forms part of UF3B's NAND inputs
// - along with UF4 output A and B
// - this will only activate when (ie UD3 Parallel to serial shift register will only latch when)
// - UE3 counts 7 AND UF4 counts 3, 7, 11, 15 (outputs A and B)
// - UF3B output goes to UD3 pin 1 LOAD(LATCH)
// - Also this then forms part of UF3C's NAND inputs
// - along with "Byte Sync Enable" (from the VIA) and
// - UC5B (NOR used to invert UF4's output B) output high when UF4 counts 0,1,4,5,8,9,12 and 13
// - (IS THIS AN OVERSIGHT IN VICE? They don't emulate it and trigger BYTE SYNC when the serial clock is high?)
// - UF3C output is then called !BYTE SYNC
//
// Read Shift Register
// UD2 74LS164 8-Bit Serial-In Parallel-Out Shift Register
// - Clocking occurs on the LOW-to-HIGH level transition of the clock input and when so, the value at the inputs is entered
// - pin 8 CLOCK - shifts the data when UF4 Counter output B (rising edge)
// - ie when UF4 counts 2, 6, 10 and 14 (2 is the only count that outputs a 1 into readShiftRegister)
// - so will only shift in a 1 on UF4 counts 2
// - on counts 6, 10 and 14 the UE5A NOR will output a 0 to pin 2 input B so no transition
// - pin 2 input B from UF4's NORed outputs C and D
// - pin 1 input A connected to VCC
// - so has no impact on the data shifted in via input B
// - On counts 2, 6, 10 and 14 of UF4 (2 is the only count that outputs a 1 into readShiftRegister)
// - will shift the data
// ie but ony a 1 will be shifed in on count 2
// - outputs Q0-Q7
//
// Sync Phase Lock
// UC2 74LS133 13 input Nand Gate
// UE4A/B 74LS74 Flop flops (These are used as an extention to UD2 shift register to increase the number of bits to 10)
// - UD2 feeds in UC2 with 8 lines
// - UE4A/B another 2
// - R/!W another 1
// - the other 2 inputs of UC2 are pulled high so SYNC is triggered only when reading and 10 consecutive 1 have been detected.
//
// Read Latch
// UC3 74LS245 Octal Bus Transceiver
// - Data read and shifted into UD2 is latched here.
// - Reading PortA of the VIA will read the values in this latch.
// - Hardware requires this latch to isolate the output of UD2 from VIA PortA when VIA PortA is set to output ie writing.
// (Note: the emulater does not need to emulate this component as its value will always reflected in the read shift register UD2)
//
// Write Shift Register
// UD3 74LS165 8-BIT Parallel to serial shift register
// - pin 2 CLOCK from UF4 Counter output B (rising edge)
// - ie when UF4 counts 2, 6, 10 and 14 (2 is the only count that outputs a 1 into readShiftRegister)
// - pin 1 LOAD form UF3B output
// - this will only activate when
// - UE3 counts 7 AND UF4 counts 3, 7, 11, 15 (outputs A and B)
//
// For some insane reason some demo coders use the write protect sensor to detect disk swapping! It is such a lame way to do it. Just use the disk ID in the sector header that's what it is for!
// I don't think I have seen any games that do this only demos. And the most popular ones tend to do it.
// When you eject a disk the disk will start to move out of the drive. Its write protect notch will no longer line up with the write protect sensor and the VIA will signal that the write protection came on.
// When the disk becomes totally ejected there is now nothing blocking the write protect sensor and the VIA will signal that the write protection was turned off.
// Now when the next disk is inserted the top of the disk will obscure the write protect sensor's LED and again the VIA will signal that the write protection came on.
// When the newly inserted disk finally comes to rest its write protect notch will now line up over the write protect sensor and again the VIA will signal that the write protection was turned off.
//
// Because of a few demos using this ridiculous technique we need to emulate the changing write protect status using a timer.
// Using a real drive you can actually break some of these demos (during the disk swap) just by the manual way you eject the disks.
#define DISK_SWAP_CYCLES_DISK_EJECTING 400000
#define DISK_SWAP_CYCLES_NO_DISK 200000
#define DISK_SWAP_CYCLES_DISK_INSERTING 400000
Drive::Drive() : m_pVIA(0)
{
Reset();
srand(0x811c9dc5U);
}
void Drive::Reset()
{
headTrackPos = 34; // Start with the head over track 18
CLOCK_SEL_AB = 3; // Track 18 will use speed zone 3 (encoder/decoder (ie UE7Counter) clocked at 1.2307Mhz)
UpdateHeadSectorPosition();
lastHeadDirection = 0;
motor = false;
SO = false;
readShiftRegister = 0;
writeShiftRegister = 0;
UE3Counter = 0;
ResetEncoderDecoder(18.0f, 22.0f);
newDiskImageQueuedCylesRemaining = DISK_SWAP_CYCLES_DISK_EJECTING + DISK_SWAP_CYCLES_NO_DISK + DISK_SWAP_CYCLES_DISK_INSERTING;
m_pVIA->InputCA1(true); // Reset in read mode
m_pVIA->InputCB1(true);
m_pVIA->InputCA2(true);
m_pVIA->InputCB2(true);
}
void Drive::Insert(DiskImage* diskImage)
{
Eject();
this->diskImage = diskImage;
newDiskImageQueuedCylesRemaining = DISK_SWAP_CYCLES_DISK_EJECTING + DISK_SWAP_CYCLES_NO_DISK + DISK_SWAP_CYCLES_DISK_INSERTING;
}
void Drive::Eject()
{
if (diskImage) diskImage = 0;
}
void Drive::DumpTrack(unsigned track)
{
if (diskImage) diskImage->DumpTrack(track);
}
// Signals from the VIA.
void Drive::OnPortOut(void* pThis, unsigned char status)
{
Drive* pDrive = (Drive*)pThis;
pDrive->motor = (status & 4) != 0;
pDrive->MoveHead(status & 3);
pDrive->CLOCK_SEL_AB = ((status >> 5) & 3);
pDrive->LED = (status & 8) != 0;
}
u32 Drive::AdvanceSectorPositionR(int& byteOffset)
{
++headBitOffset %= bitsInTrack;
byteOffset = headBitOffset >> 3;
return (~headBitOffset) & 7;
}
u32 Drive::AdvanceSectorPositionW(int& byteOffset)
{
byteOffset = headBitOffset >> 3;
u32 bit = (~headBitOffset) & 7;
++headBitOffset %= bitsInTrack;
return bit;
}
bool Drive::GetNextBit()
{
int byteOffset;
int bit = AdvanceSectorPositionR(byteOffset);
return diskImage->GetNextBit(headTrackPos, byteOffset, bit);
}
void Drive::SetNextBit(bool value)
{
int byteOffset;
int bit = AdvanceSectorPositionW(byteOffset);
diskImage->SetBit(headTrackPos, byteOffset, bit, value);
}
bool Drive::Update()
{
bool dataReady = false;
// When swapping some lame loaders monitor the write protect flag.
// Bit 4 of PortB (WP - write protect) should be;
// X Write protect status of D1
// 0 Write protected (D1 ejecting)
// 1 Not write protected (no disk)
// 0 Write protected (D2 inserting)
// X Write protect status of D2
if (newDiskImageQueuedCylesRemaining > 0)
{
newDiskImageQueuedCylesRemaining--;
if (newDiskImageQueuedCylesRemaining == 0) m_pVIA->GetPortB()->SetInput(0x10, !diskImage->GetReadOnly()); // X Write protect status of D2
else if (newDiskImageQueuedCylesRemaining > DISK_SWAP_CYCLES_NO_DISK + DISK_SWAP_CYCLES_DISK_INSERTING) m_pVIA->GetPortB()->SetInput(0x10, false); // 0 Write protected (D1 ejecting)
else if (newDiskImageQueuedCylesRemaining > DISK_SWAP_CYCLES_DISK_INSERTING) m_pVIA->GetPortB()->SetInput(0x10, true); // 1 Not write protected (no disk)
else m_pVIA->GetPortB()->SetInput(0x10, false); // 0 Write protected (D2 inserting)
}
else if (diskImage && motor)
{
bool writing = (m_pVIA->GetFCR() & m6522::FCR_CB2_OUTPUT_MODE0) == 0;
if (SO)
{
dataReady = true;
SO = false;
}
// UE6 provides the CPU's clock by dividing the 16Mhz clock by 16.
// UE7 (a 74ls193 4bit counter) counts up on the falling edge of the 16Mhz clock. UE7 drives the Encoder/Decoder clock.
// So we need to simulate 16 cycles for every 1 CPU cycle
for (int cycles = 0; cycles < 16; ++cycles)
{
if (!writing)
{
if (++cyclesForBit >= cyclesPerBit)
{
cyclesForBit -= cyclesPerBit;
// Any 1 bit coming from the disk will come in the form of a flux reversal. (Non return to zero inverted emulation.)
if (GetNextBit())
{
// We have a genuine flux reversal.
// Pin 12 of UE5D is the BIT SYNC Input. When a positive pulse is applied to pin 12, the output of UE5D(pin 13) is applied to the load line (of UE7),
// causing the encoder/decoder clock to terminate the current cycle early and begin a new one.
ResetEncoderDecoder(18.0f, 20.0f); // Start seeing random flux reversals 18us-20us from now (ie since the last real flux reversal).
}
}
// The video amplifiers will often oscillate with no data in, but these oscillations are high enough in frequency that they "seldom" get past the valid pulse detector.
// Some do and some copy protections rely on this random behaviour so we need to emultate it.
// For example, 720 will read a byte from the disk multiple times and check that the values read each time were infact different. It does not matter what the values are just that they are different.
randomFluxReversalTime -= 0.0625f; // One 16th of a micro second.
if (randomFluxReversalTime <= 0) ResetEncoderDecoder(2.0f, 25.0f); // Trigger a random noise generated zero crossing and start seeing more anywhere between 2us and 25us after this one.
}
if (++UE7Counter == 0x10) // The count carry (bit 4) clocks UF4.
{
UE7Counter = CLOCK_SEL_AB; // A and B inputs of UE7 come from the VIA's CLOCK SEL A/B outputs (ie PB5/6) ie preload the encoder/decoder clock for the current density settings.
// The decoder consists of UF4 and UE5A. The ecoder has two outputs, Pin 1 of UE5A is the serial data output and pin 2 of UF4 (output B) is the serial clock output.
++UF4Counter &= 0xf; // Clock and clamp UF4.
// The UD2 read shift register is clocked by serial clock (the rising edge of encoder/decoder's UF4 B output (serial clock))
// - ie on counts 2, 6, 10 and 14 (2 is the only count that outputs a 1 into readShiftRegister as the MSB bits of the count NORed together for other values are 0)
if ((UF4Counter & 0x3) == 2)
{
// A bit cell is four encoder/decoder clock pulses wide, as the 2nd bit of UF4 controls the serial clock (and takes 4 cycles to loop a two bit counter).
// If a flux reversal (or pulse into the decoder) occurs at the beginning of a cell, that cell is a 1 else that cell is a 0.
// If a flux reversal occurs, UF4's counter is cleared and the timing circuit is reset to start the encoder/decoder clock at the beginning of the VIA's current density setting.
// Pins 6 (output C) and 7 (output D) of UF4 are low, causing the output of UE5A, the serial data line, to go high.
// 2 encoder/decoder clock pulses later, the serial clock(pin 2 of UF4) goes high. When the serial clock line is high, the serial data line is valid and the shift register will shift in the data.
// The serial clock line remains high for another clock cycle.
// After four encoder/decoder clocks a bit cell is now complete.
// At this time, pins 2 (output A) and 3 (output B) of UF4 will again be low but as the count is counting up pin 6 (output C) will now be high.
// The high on pin 6 (output C) of UF4 causes the serial data line (pin 1 of UE5A) to go low as this is NORed with the low on pin 7 (output D).
// If a flux reversal occurs at the beginning of the next cell then everything resets and again we see a 1 on the serial data line 2 encoder/decoder cycles into that cell.
// If no flux reversal occurs at the beginning of the next cell, the serial data line will remain low when the serial clock line goes high again (two encoder/decoder clock cycles into the new cell).
// If there are no flux reversals for 2 cells then we see 0 on pin 6 (output C) and 1 on pin 7 (output D) of UF4 and this causes the serial data line (pin 1 of UE5A) to remain at 0.
// If there are no flux reversals for 3 cells then we see 1 on pin 6 (output C) and 1 on pin 7 (output D) of UF4 and this causes the serial data line (pin 1 of UE5A) to also remain at 0, after all, UE5A is a NOR gate.
// After 4 cells the counter inside UF4 loops back to 0 and we again see 0 on pin 6 (output C) and 0 on pin 7 (output C), causing the output of UE5A, the serial data line, to go to a 1, regardless of a true flux reversal!
readShiftRegister <<= 1;
readShiftRegister |= (UF4Counter == 2); // Emulate UE5A and only shift in a 1 when pins 6 (output C) and 7 (output D) (bits 2 and 3 of UF4Counter are 0. ie the first count of the bit cell)
if (writing) SetNextBit((writeShiftRegister & 0x80));
writeShiftRegister <<= 1;
// Note: SYNC can only trigger during reading as R/!W line is one of UC2's inputs.
if (!writing && ((readShiftRegister & 0x3ff) == 0x3ff)) // if the last 10 bits are 1s then SYNC
{
UE3Counter = 0; // Phase lock on to byte boundary
m_pVIA->GetPortB()->SetInput(0x80, false); // PB7 active low SYNC
}
else
{
if (!writing) m_pVIA->GetPortB()->SetInput(0x80, true); // SYNC not asserted if not following the SYNC bits
UE3Counter++;
}
}
// UC5B (NOR used to invert UF4's output B serial clock) output high when UF4 counts 0,1,4,5,8,9,12 and 13
else if (((UF4Counter & 2) == 0) && (UE3Counter == 8)) // Phase locked on to byte boundary
{
UE3Counter = 0;
SO = (m_pVIA->GetFCR() & m6522::FCR_CA2_OUTPUT_MODE0) != 0; // bit 2 of the FCR indicates "Byte Ready Active" turned on or not.
if (writing)
{
writeShiftRegister = m_pVIA->GetPortA()->GetOutput();
}
else
{
writeShiftRegister = (u8)(readShiftRegister & 0xff);
m_pVIA->GetPortA()->SetInput(writeShiftRegister);
}
}
}
}
}
m_pVIA->InputCA1(!SO);
return dataReady;
}

128
Drive.h Normal file
View File

@ -0,0 +1,128 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#ifndef DRIVE_H
#define DRIVE_H
#include "m6522.h"
#include "DiskImage.h"
#include <stdlib.h>
class Drive
{
public:
Drive();
void SetVIA(m6522* pVIA)
{
m_pVIA = pVIA;
pVIA->GetPortB()->SetPortOut(this, OnPortOut);
}
static void OnPortOut(void*, unsigned char status);
bool Update();
void Insert(DiskImage* diskImage);
inline const DiskImage* GetDiskImage() const { return diskImage; }
void Eject();
void Reset();
inline unsigned Track() const { return headTrackPos; }
inline unsigned SectorPos() const { return headBitOffset >> 3; }
inline unsigned GetHeadBitOffset() const { return headBitOffset; }
inline bool IsMotorOn() const { return motor; }
inline bool IsLEDOn() const { return LED; }
inline unsigned char GetLastHeadDirection() const { return lastHeadDirection; } // For simulated head movement sounds
private:
inline float GenerateRandomFluxReversalTime(float min, float max) { return ((max - min) * ((float)rand() / RAND_MAX)) + min; } // Inputs in micro seconds
inline void ResetEncoderDecoder(float min, float max)
{
UE7Counter = CLOCK_SEL_AB; // A and B inputs of UE7 come from the VIA's CLOCK SEL A/B outputs (ie PB5/6)
UF4Counter = 0;
randomFluxReversalTime = GenerateRandomFluxReversalTime(min, max);
}
inline void UpdateHeadSectorPosition()
{
// Disk spins at 300rpm = 5rps so to calculate how many 16Mhz cycles one rotation takes;-
// 16000000 / 5 = 3200000;
static const float CYCLES_16Mhz_PER_ROTATION = 3200000.0f;
bitsInTrack = diskImage->BitsInTrack(headTrackPos);
headBitOffset %= bitsInTrack;
cyclesPerBit = CYCLES_16Mhz_PER_ROTATION / (float)bitsInTrack;
}
inline void MoveHead(unsigned char headDirection)
{
if (lastHeadDirection != headDirection)
{
if (((lastHeadDirection - 1) & 3) == headDirection)
{
if (headTrackPos > 0) headTrackPos--;
// else head bang
}
else if (((lastHeadDirection + 1) & 3) == headDirection)
{
if (headTrackPos < HALF_TRACK_COUNT - 1) headTrackPos++;
}
lastHeadDirection = headDirection;
UpdateHeadSectorPosition();
}
}
void DumpTrack(unsigned track); // Used for debugging disk images.
u32 AdvanceSectorPositionR(int& byte_offset); // No reason why I seperate these into individual read and write versions. I was just trying to get the bit stream to line up when rewriting over existing data.
u32 AdvanceSectorPositionW(int& byte_offset);
bool GetNextBit();
void SetNextBit(bool value);
DiskImage* diskImage;
// When swapping disks some code waits for the write protect signal to go high which will happen if a human ejects a disk.
// Emulate this by asserting the write protect signal for a few cycles before inserting the new disk image.
u32 newDiskImageQueuedCylesRemaining;
// CA1 (input)
// - !BYTE SYNC
// CA2 (output)
// - BYTE SYNC enable
// CB1 (NC)
// - check pulled H/L
// CB2 (output)
// - R/!W
m6522* m_pVIA;
float cyclesForBit;
u32 readShiftRegister;
unsigned headTrackPos;
u32 headBitOffset;
float randomFluxReversalTime;
int UE7Counter;
int UF4Counter;
int UE3Counter;
int CLOCK_SEL_AB;
bool SO;
unsigned char lastHeadDirection;
u32 bitsInTrack;
float cyclesPerBit;
bool motor;
bool LED;
u8 writeShiftRegister;
};
#endif

932
FileBrowser.cpp Normal file
View File

@ -0,0 +1,932 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#include "FileBrowser.h"
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <algorithm>
#include "Screen.h"
#include "debug.h"
#include "Keyboard.h"
#include "options.h"
#include "InputMappings.h"
#include "stb_image.h"
#include "Petscii.h"
extern "C"
{
#include "rpi-gpio.h"
}
extern Screen screen;
#define PNG_WIDTH 320
#define PNG_HEIGHT 200
unsigned char FileBrowser::LSTBuffer[FileBrowser::LSTBuffer_size];
const unsigned FileBrowser::SwapKeys[30] =
{
KEY_F1, KEY_KP1, KEY_1,
KEY_F2, KEY_KP2, KEY_2,
KEY_F3, KEY_KP3, KEY_3,
KEY_F4, KEY_KP4, KEY_4,
KEY_F5, KEY_KP5, KEY_5,
KEY_F6, KEY_KP6, KEY_6,
KEY_F7, KEY_KP7, KEY_7,
KEY_F8, KEY_KP8, KEY_8,
KEY_F9, KEY_KP9, KEY_9,
KEY_F10, KEY_KP0, KEY_0
};
static const u32 palette[] =
{
RGBA(0x00, 0x00, 0x00, 0xFF),
RGBA(0xFF, 0xFF, 0xFF, 0xFF),
RGBA(0x88, 0x39, 0x32, 0xFF),
RGBA(0x67, 0xB6, 0xBD, 0xFF),
RGBA(0x8B, 0x4F, 0x96, 0xFF),
RGBA(0x55, 0xA0, 0x49, 0xFF),
RGBA(0x40, 0x31, 0x8D, 0xFF),
RGBA(0xBF, 0xCE, 0x72, 0xFF),
RGBA(0x8B, 0x54, 0x29, 0xFF),
RGBA(0x57, 0x42, 0x00, 0xFF),
RGBA(0xB8, 0x69, 0x62, 0xFF),
RGBA(0x50, 0x50, 0x50, 0xFF),
RGBA(0x78, 0x78, 0x78, 0xFF),
RGBA(0x94, 0xE0, 0x89, 0xFF),
RGBA(0x78, 0x69, 0xC4, 0xFF),
RGBA(0x9F, 0x9F, 0x9F, 0xFF)
};
FileBrowser::BrowsableList::Entry* FileBrowser::BrowsableList::FindEntry(const char* name)
{
int index;
int len = (int)entries.size();
for (index = 0; index < len; ++index)
{
Entry* entry = &entries[index];
if (!(entry->filImage.fattrib & AM_DIR) && strcasecmp(name, entry->filImage.fname) == 0)
return entry;
}
return 0;
}
FileBrowser::FileBrowser(DiskCaddy* diskCaddy, ROMs* roms, unsigned deviceID, bool displayPNGIcons)
: state(State_Folders)
, maxOnScreen(38)
, diskCaddy(diskCaddy)
, selectionsMade(false)
, roms(roms)
, deviceID(deviceID)
, displayPNGIcons(displayPNGIcons)
{
}
u32 FileBrowser::Colour(int index)
{
return palette[index & 0xf];
}
struct greater
{
bool operator()(const FileBrowser::BrowsableList::Entry& lhs, const FileBrowser::BrowsableList::Entry& rhs) const
{
if (strcasecmp(lhs.filImage.fname, "..") == 0)
return true;
else if (strcasecmp(rhs.filImage.fname, "..") == 0)
return false;
else if (((lhs.filImage.fattrib & AM_DIR) && (rhs.filImage.fattrib & AM_DIR)) || (!(lhs.filImage.fattrib & AM_DIR) && !(rhs.filImage.fattrib & AM_DIR)))
return strcasecmp(lhs.filImage.fname, rhs.filImage.fname) < 0;
else if ((lhs.filImage.fattrib & AM_DIR) && !(rhs.filImage.fattrib & AM_DIR))
return true;
else
return false;
}
};
void FileBrowser::RefreshFolderEntries()
{
DIR dir;
FileBrowser::BrowsableList::Entry entry;
FRESULT res;
char* ext;
folder.Clear();
res = f_opendir(&dir, ".");
if (res == FR_OK)
{
do
{
res = f_readdir(&dir, &entry.filImage);
ext = strrchr(entry.filImage.fname, '.');
if (res == FR_OK && entry.filImage.fname[0] != 0 && !(ext && strcasecmp(ext, ".png") == 0))
folder.entries.push_back(entry);
}
while (res == FR_OK && entry.filImage.fname[0] != 0);
f_closedir(&dir);
// Now check for icons
res = f_opendir(&dir, ".");
if (res == FR_OK)
{
do
{
res = f_readdir(&dir, &entry.filIcon);
ext = strrchr(entry.filIcon.fname, '.');
if (ext)
{
int length = ext - entry.filIcon.fname;
if (res == FR_OK && entry.filIcon.fname[0] != 0 && strcasecmp(ext, ".png") == 0)
{
for (unsigned index = 0; index < folder.entries.size(); ++index)
{
FileBrowser::BrowsableList::Entry* entryAtIndex = &folder.entries[index];
if (strncasecmp(entry.filIcon.fname, entryAtIndex->filImage.fname, length) == 0)
entryAtIndex->filIcon = entry.filIcon;
}
}
}
}
while (res == FR_OK && entry.filIcon.fname[0] != 0);
}
f_closedir(&dir);
strcpy(entry.filImage.fname, "..");
entry.filIcon.fname[0] = 0;
folder.entries.push_back(entry);
std::sort(folder.entries.begin(), folder.entries.end(), greater());
if (folder.entries.size() > 0) folder.current = &folder.entries[0];
else folder.current = 0;
folder.currentIndex = 0;
}
else
{
//DEBUG_LOG("Cannot open dir");
}
// incase they deleted something selected in the caddy
caddySelections.Clear();
}
void FileBrowser::FolderChanged()
{
RefreshFolderEntries();
RefeshDisplay();
}
void FileBrowser::DisplayRoot()
{
f_chdir("\\1541");
FolderChanged();
}
void FileBrowser::RefeshDisplayForBrowsableList(FileBrowser::BrowsableList* browsableList, int xOffset, bool showSelected)
{
char buffer1[128] = { 0 };
char buffer2[128] = { 0 };
u32 index;
u32 entryIndex;
u32 x = xOffset;
u32 y = 17;
u32 colour;
bool terminal = false;
RGBA BkColour = RGBA(0, 0, 0, 0xFF); //palette[VIC2_COLOUR_INDEX_BLUE];
if (terminal)
printf("\E[2J\E[f");
for (index = 0; index < maxOnScreen; ++index)
{
entryIndex = browsableList->offset + index;
if (entryIndex < browsableList->entries.size())
{
FileBrowser::BrowsableList::Entry* entry = &browsableList->entries[entryIndex];
snprintf(buffer2, 81, "%s", entry->filImage.fname);
memset(buffer1, ' ', 80);
buffer1[127] = 0;
strncpy(buffer1, buffer2, strlen(buffer2));
if (showSelected && browsableList->currentIndex == entryIndex)
{
if (entry->filImage.fattrib & AM_DIR)
{
if (terminal)
printf("\E[34;47m%s\E[0m\r\n", buffer1);
screen.PrintText(false, x, y, buffer1, palette[VIC2_COLOUR_INDEX_LBLUE], RGBA(0xff, 0xff, 0xff, 0xff));
}
else
{
colour = RGBA(0xff, 0, 0, 0xff);
if (entry->filImage.fattrib & AM_RDO)
colour = palette[VIC2_COLOUR_INDEX_RED];
screen.PrintText(false, x, y, buffer1, colour, RGBA(0xff, 0xff, 0xff, 0xff));
if (terminal)
printf("\E[31;47m%s\E[0m\r\n", buffer1);
}
}
else
{
if (entry->filImage.fattrib & AM_DIR)
{
screen.PrintText(false, x, y, buffer1, palette[VIC2_COLOUR_INDEX_LBLUE], BkColour);
if (terminal)
printf("\E[34m%s\E[0m\r\n", buffer1);
}
else
{
colour = palette[VIC2_COLOUR_INDEX_LGREY];
if (entry->filImage.fattrib & AM_RDO)
colour = palette[VIC2_COLOUR_INDEX_PINK];
screen.PrintText(false, x, y, buffer1, colour, BkColour);
if (terminal)
printf("\E[0;m%s\E[0m\r\n", buffer1);
}
}
}
else
{
memset(buffer1, ' ', 80);
screen.PrintText(false, x, y, buffer1, BkColour, BkColour);
if (terminal)
printf("%s\r\n", buffer1);
}
y += 16;
}
}
void FileBrowser::RefeshDisplay()
{
char buffer[1024];
if (f_getcwd(buffer, 1024) == FR_OK)
{
u32 textColour = Colour(VIC2_COLOUR_INDEX_LGREEN);
u32 bgColour = Colour(VIC2_COLOUR_INDEX_GREY);
screen.ClearArea(0, 0, (int)screen.Width(), 17, bgColour);
screen.PrintText(false, 0, 0, buffer, textColour, bgColour);
}
RefeshDisplayForBrowsableList(&folder, 0);
RefeshDisplayForBrowsableList(&caddySelections, 1024 - 320, false);
DisplayPNG();
DisplayStatusBar();
}
bool FileBrowser::CheckForPNG(const char* filename, FILINFO& filIcon)
{
bool foundValid = false;
filIcon.fname[0] = 0;
if (DiskImage::IsDiskImageExtention(filename))
{
char fileName[256];
char* ptr = strrchr(filename, '.');
if (ptr)
{
int len = ptr - filename;
strncpy(fileName, filename, len);
fileName[len] = 0;
strcat(fileName, ".png");
if (f_stat(fileName, &filIcon) == FR_OK && filIcon.fsize < FILEBROWSER_MAX_PNG_SIZE)
foundValid = true;
}
}
return foundValid;
}
void FileBrowser::DisplayPNG(FILINFO& filIcon, int x, int y)
{
if (filIcon.fname[0] != 0 && filIcon.fsize < FILEBROWSER_MAX_PNG_SIZE)
{
FIL fp;
FRESULT res;
res = f_open(&fp, filIcon.fname, FA_READ);
if (res == FR_OK)
{
u32 bytesRead;
SetACTLed(true);
f_read(&fp, PNG, FILEBROWSER_MAX_PNG_SIZE, &bytesRead);
SetACTLed(false);
f_close(&fp);
int w;
int h;
int channels_in_file;
stbi_uc* image = stbi_load_from_memory((stbi_uc const*)PNG, bytesRead, &w, &h, &channels_in_file, 4);
if (image && (w == PNG_WIDTH && h == PNG_HEIGHT))
{
//DEBUG_LOG("Opened PNG %s w = %d h = %d cif = %d\r\n", fileName, w, h, channels_in_file);
screen.PlotImage((u32*)image, x, y, w, h);
}
else
{
//DEBUG_LOG("Invalid PNG size %d x %d\r\n", w, h);
}
}
}
else
{
//DEBUG_LOG("Cannot find PNG %s\r\n", fileName);
}
}
void FileBrowser::DisplayPNG()
{
if (displayPNGIcons && folder.current)
{
FileBrowser::BrowsableList::Entry* current = folder.current;
DisplayPNG(current->filIcon, 1024 - 320, 426);
}
}
void FileBrowser::PopFolder()
{
f_chdir("..");
//{
// char buffer[1024];
// if (f_getcwd(buffer, 1024) == FR_OK)
// {
// DEBUG_LOG("CWD = %s\r\n", buffer);
// }
//}
RefreshFolderEntries();
caddySelections.Clear();
RefeshDisplay();
}
void FileBrowser::UpdateInput()
{
InputMappings* inputMappings = InputMappings::Instance();
Keyboard* keyboard = Keyboard::Instance();
bool dirty = false;
if (keyboard->CheckChanged())
dirty = inputMappings->CheckKeyboardBrowseMode();
else
dirty = inputMappings->CheckButtonsBrowseMode();
if (dirty)
{
//if (state == State_Folders)
UpdateInputFolders();
//else
// UpdateInputDiskCaddy();
}
}
FileBrowser::Folder* FileBrowser::GetCurrentFolder()
{
return &folder;
}
bool FileBrowser::FillCaddyWithSelections()
{
if (caddySelections.entries.size())
{
for (auto it = caddySelections.entries.begin(); it != caddySelections.entries.end();)
{
bool readOnly = ((*it).filImage.fattrib & AM_RDO) != 0;
if (diskCaddy->Insert(&(*it).filImage, readOnly) == false)
caddySelections.entries.erase(it);
else
it++;
}
return true;
}
return false;
}
bool FileBrowser::AddToCaddy(FileBrowser::BrowsableList::Entry* current)
{
bool added = false;
if (current && !(current->filImage.fattrib & AM_DIR) && DiskImage::IsDiskImageExtention(current->filImage.fname))
{
bool canAdd = true;
unsigned i;
for (i = 0; i < caddySelections.entries.size(); ++i)
{
if (strcmp(current->filImage.fname, caddySelections.entries[i].filImage.fname) == 0)
{
canAdd = false;
break;
}
}
if (canAdd)
{
caddySelections.entries.push_back(*current);
added = true;
}
}
return added;
}
void FileBrowser::UpdateInputFolders()
{
Keyboard* keyboard = Keyboard::Instance();
InputMappings* inputMappings = InputMappings::Instance();
if (folder.entries.size() > 0)
{
u32 numberOfEntriesMinus1 = folder.entries.size() - 1;
bool dirty = false;
if (inputMappings->BrowseSelect())
{
FileBrowser::BrowsableList::Entry* current = folder.current;
if (current)
{
if (current->filImage.fattrib & AM_DIR)
{
if (strcmp(current->filImage.fname, "..") == 0)
{
PopFolder();
}
else if (strcmp(current->filImage.fname, ".") != 0)
{
f_chdir(current->filImage.fname);
RefreshFolderEntries();
}
dirty = true;
}
else
{
if (strcmp(current->filImage.fname, "..") == 0)
{
PopFolder();
}
else if (DiskImage::IsDiskImageExtention(current->filImage.fname))
{
DiskImage::DiskType diskType = DiskImage::GetDiskImageTypeViaExtention(current->filImage.fname);
// Should also be able to create a LST file from all the images currently selected in the caddy
if (diskType == DiskImage::LST)
{
selectionsMade = SelectLST(current->filImage.fname);
}
else
{
// Add the current selected
AddToCaddy(current);
selectionsMade = FillCaddyWithSelections();
}
if (selectionsMade)
lastSelectionName = current->filImage.fname;
dirty = true;
}
}
}
}
else if (inputMappings->BrowseDone())
{
selectionsMade = FillCaddyWithSelections();
}
//else if (keyboard->KeyPressed(KEY_TAB))
//{
// state = State_DiskCaddy;
// dirty = true;
//}
else if (inputMappings->BrowseBack())
{
PopFolder();
dirty = true;
}
else if (inputMappings->Exit())
{
caddySelections.Clear();
dirty = true;
}
else if (inputMappings->BrowseInsert())
{
FileBrowser::BrowsableList::Entry* current = folder.current;
if (current)
{
dirty = AddToCaddy(current);
}
}
else
{
unsigned keySetIndex;
for (keySetIndex = 0; keySetIndex < ROMs::MAX_ROMS; ++keySetIndex)
{
unsigned keySetIndexBase = keySetIndex * 3;
if (keyboard->KeyPressed(FileBrowser::SwapKeys[keySetIndexBase]) || keyboard->KeyPressed(FileBrowser::SwapKeys[keySetIndexBase + 1]) || keyboard->KeyPressed(FileBrowser::SwapKeys[keySetIndexBase + 2]))
{
if (roms->ROMValid[keySetIndex])
{
roms->currentROMIndex = keySetIndex;
roms->lastManualSelectedROMIndex = keySetIndex;
DEBUG_LOG("Swap ROM %d %s\r\n", keySetIndex, roms->ROMNames[keySetIndex]);
ShowDeviceAndROM();
}
}
}
if (inputMappings->BrowseDown())
{
if (folder.currentIndex < numberOfEntriesMinus1)
{
folder.currentIndex++;
folder.current = &folder.entries[folder.currentIndex];
if (folder.currentIndex >= (folder.offset + maxOnScreen) && (folder.currentIndex < folder.entries.size()))
folder.offset++;
dirty = true;
}
}
if (inputMappings->BrowseUp())
{
if (folder.currentIndex > 0)
{
folder.currentIndex--;
folder.current = &folder.entries[folder.currentIndex];
if ((folder.offset > 0) && (folder.currentIndex < folder.offset))
folder.offset--;
dirty = true;
}
}
if (inputMappings->BrowsePageDown())
{
u32 maxOnScreenMinus1 = maxOnScreen - 1;
if (folder.currentIndex == folder.offset + maxOnScreenMinus1)
{
// Need to move the screen window down so that the currentIndex is now at the top of the screen
folder.offset = folder.currentIndex;
// Current index now becomes the bottom one
if (folder.offset + maxOnScreenMinus1 > numberOfEntriesMinus1)
folder.currentIndex = numberOfEntriesMinus1; // Not enough entries to move another page just move to the last entry
else // Move the window down a page
folder.currentIndex = folder.offset + maxOnScreenMinus1;
}
else
{
// Need to move to folder.offset + maxOnScreenMinus1
if (folder.offset + maxOnScreenMinus1 > numberOfEntriesMinus1)
folder.currentIndex = numberOfEntriesMinus1; // Run out of entries before we hit the bottom. Just move to the bottom.
else
folder.currentIndex = folder.offset + maxOnScreenMinus1; // Move the bottom of the screen
}
folder.current = &folder.entries[folder.currentIndex];
dirty = true;
}
if (inputMappings->BrowsePageUp())
{
if (folder.currentIndex == folder.offset)
{
// If the cursor is already at the top of the window then page up
int offset = (int)folder.currentIndex - (int)maxOnScreen;
if (offset < 0) folder.offset = 0;
else folder.offset = (u32)offset;
folder.currentIndex = folder.offset;
}
else
{
folder.currentIndex = folder.offset; // Move the cursor to the top of the window
}
folder.current = &folder.entries[folder.currentIndex];
dirty = true;
}
}
if (dirty) RefeshDisplay();
}
else
{
if (inputMappings->BrowseBack())
PopFolder();
}
}
bool FileBrowser::SelectLST(const char* filenameLST)
{
bool validImage = false;
//DEBUG_LOG("Selected %s\r\n", filenameLST);
if (DiskImage::IsLSTExtention(filenameLST))
{
FileBrowser::Folder* currentFolder = GetCurrentFolder();
if (currentFolder)
{
DiskImage::DiskType diskType;
FIL fp;
FRESULT res;
res = f_open(&fp, filenameLST, FA_READ);
if (res == FR_OK)
{
u32 bytesRead;
SetACTLed(true);
f_read(&fp, FileBrowser::LSTBuffer, FileBrowser::LSTBuffer_size, &bytesRead);
SetACTLed(false);
f_close(&fp);
TextParser textParser;
textParser.SetData((char*)FileBrowser::LSTBuffer);
char* token = textParser.GetToken(true);
while (token)
{
//DEBUG_LOG("LST token = %s\r\n", token);
diskType = DiskImage::GetDiskImageTypeViaExtention(token);
if (diskType == DiskImage::D64 || diskType == DiskImage::G64 || diskType == DiskImage::NIB || diskType == DiskImage::NBZ)
{
FileBrowser::BrowsableList::Entry* entry = currentFolder->FindEntry(token);
if (entry && !(entry->filImage.fattrib & AM_DIR))
{
bool readOnly = (entry->filImage.fattrib & AM_RDO) != 0;
if (diskCaddy->Insert(&entry->filImage, readOnly))
validImage = true;
}
}
else
{
roms->SelectROM(token);
}
token = textParser.GetToken(true);
}
}
}
}
return validImage;
}
// Not used
void FileBrowser::UpdateInputDiskCaddy()
{
bool dirty = false;
Keyboard* keyboard = Keyboard::Instance();
if (keyboard->KeyPressed(KEY_DELETE))
{
}
else if (keyboard->KeyPressed(KEY_TAB))
{
state = State_Folders;
dirty = true;
}
else
{
if (keyboard->KeyHeld(KEY_DOWN))
{
}
if (keyboard->KeyHeld(KEY_UP))
{
}
}
if (dirty) RefeshDisplay();
}
void FileBrowser::DisplayStatusBar()
{
u32 x = 0;
u32 y = STATUS_BAR_POSITION_Y;
char bufferOut[128];
snprintf(bufferOut, 256, "LED 0 Motor 0 Track 00.0 ATN 0 DAT 0 CLK 0");
screen.PrintText(false, x, y, bufferOut, RGBA(0, 0, 0, 0xff), RGBA(0xff, 0xff, 0xff, 0xff));
}
void FileBrowser::ClearScreen()
{
u32 bgColour = palette[VIC2_COLOUR_INDEX_BLUE];
screen.Clear(bgColour);
}
void FileBrowser::ShowDeviceAndROM()
{
char buffer[256];
u32 textColour = RGBA(0, 0, 0, 0xff);
u32 bgColour = RGBA(0xff, 0xff, 0xff, 0xff);
u32 y = STATUS_BAR_POSITION_Y;
snprintf(buffer, 256, "Device %d %s \r\n", deviceID, roms->ROMNames[roms->currentROMIndex]);
screen.PrintText(false, 43 * 8, y, buffer, textColour, bgColour);
}
void FileBrowser::DisplayDiskInfo(DiskImage* diskImage, const char* filenameForIcon)
{
// Ideally we should not have to load the entire disk to read the directory.
static const char* fileTypes[]=
{
"DEL", "SEQ", "PRG", "USR", "REL", "UKN", "UKN", "UKN"
};
// Decode the BAM
unsigned track = 18;
unsigned sectorNo = 0;
char name[17] = { 0 };
unsigned char buffer[260] = { 0 };
int charIndex;
u32 x = 0;
u32 y = 0;
char bufferOut[128] = { 0 };
u32 textColour = palette[VIC2_COLOUR_INDEX_LBLUE];
u32 bgColour = palette[VIC2_COLOUR_INDEX_BLUE];
u32 usedColour = palette[VIC2_COLOUR_INDEX_RED];
u32 freeColour = palette[VIC2_COLOUR_INDEX_LGREEN];
ClearScreen();
if (diskImage->GetDecodedSector(track, sectorNo, buffer))
{
track = buffer[0];
sectorNo = buffer[1];
//144-161 ($90-Al) Name of the disk (padded with "shift space")
//162,163 ($A2,$A3) Disk ID marker
//164 ($A4) $A0 Shift Space
//165,166 ($A5,$A6) $32,$41 ASCII chars "2A" DOS indicator
//167-170 ($A7-$AA) $A0 Shift Space
//171-255 ($AB-$FF) $00 Not used, filled with zero (The bytes 180 to 191 can have the contents "blocks free" on many disks.)
strncpy(name, (char*)&buffer[144], 16);
int blocksFree = 0;
int bamTrack;
int lastTrackUsed = (int)diskImage->LastTrackUsed() >> 1;
for (bamTrack = 0; bamTrack < 35; ++bamTrack)
{
if ((bamTrack + 1) != 18)
blocksFree += buffer[BAM_OFFSET + bamTrack * BAM_ENTRY_SIZE];
x = 400;
for (int bit = 0; bit < DiskImage::SectorsPerTrack[bamTrack]; bit++)
{
u32 bits = buffer[BAM_OFFSET + 1 + (bit >> 3) + bamTrack * BAM_ENTRY_SIZE];
bool used = (bits & (1 << (bit & 0x7))) == 0;
if (!used)
{
snprintf(bufferOut, 128, "%c", screen2petscii(87));
screen.PrintText(true, x, y, bufferOut, usedColour, bgColour);
}
else
{
snprintf(bufferOut, 128, "%c", screen2petscii(81));
screen.PrintText(true, x, y, bufferOut, freeColour, bgColour);
}
x += 8;
bits <<= 1;
}
y += 8;
}
for (; bamTrack < lastTrackUsed; ++bamTrack)
{
x = 400;
for (int bit = 0; bit < DiskImage::SectorsPerTrack[bamTrack]; bit++)
{
snprintf(bufferOut, 128, "%c", screen2petscii(87));
screen.PrintText(true, x, y, bufferOut, usedColour, bgColour);
x += 8;
}
y += 8;
}
x = 0;
y = 0;
snprintf(bufferOut, 128, "0");
screen.PrintText(true, x, y, bufferOut, textColour, bgColour);
x = 16;
snprintf(bufferOut, 128, "\"%s\" %c%c%c%c%c%c", name, buffer[162], buffer[163], buffer[164], buffer[165], buffer[166], buffer[167]);
screen.PrintText(true, x, y, bufferOut, bgColour, textColour);
x = 0;
y += 8;
if (track != 0)
{
bool complete = false;
// Blocks 1 through 19 on track 18 contain the file entries. The first two bytes of a block point to the next directory block with file entries. If no more directory blocks follow, these bytes contain $00 and $FF, respectively.
while (!complete)
{
//DEBUG_LOG("track %d sector %d\r\n", track, sectorNo);
if (diskImage->GetDecodedSector(track, sectorNo, buffer))
{
unsigned trackNext = buffer[0];
unsigned sectorNoNext = buffer[1];
complete = (track == trackNext) && (sectorNo == sectorNoNext); // Detect looping directory entries (raid over moscow ntsc)
complete |= (trackNext == 00) || (sectorNoNext == 0xff);
complete |= (trackNext == 18) && (sectorNoNext == 1);
track = trackNext;
sectorNo = sectorNoNext;
int entry;
int entryOffset = 2;
for (entry = 0; entry < 8; ++entry)
{
bool done = true;
for (int i = 0; i < 0x1d; ++i)
{
if (buffer[i + entryOffset])
done = false;
}
if (!done)
{
u8 fileType = buffer[DIR_ENTRY_OFFSET_TYPE + entryOffset];
u16 blocks = (buffer[DIR_ENTRY_OFFSET_BLOCKS + entryOffset + 1] << 8) | buffer[DIR_ENTRY_OFFSET_BLOCKS + entryOffset];
x = 0;
for (charIndex = 0; charIndex < DIR_ENTRY_NAME_LENGTH; ++charIndex)
{
char c = buffer[DIR_ENTRY_OFFSET_NAME + entryOffset + charIndex];
if (c == 0xa0) c = 0x20;
name[charIndex] = c;
}
name[charIndex] = 0;
//DEBUG_LOG("%d name = %s %x\r\n", blocks, name, fileType);
snprintf(bufferOut, 128, "%d", blocks);
screen.PrintText(true, x, y, bufferOut, textColour, bgColour);
x += 5 * 8;
snprintf(bufferOut, 128, "\"%s\"", name);
screen.PrintText(true, x, y, bufferOut, textColour, bgColour);
x += 19 * 8;
char modifier = 0x20;
if ((fileType & 0x80) == 0)
modifier = screen2petscii(42);
else if (fileType & 0x40)
modifier = screen2petscii(60);
snprintf(bufferOut, 128, "%s%c", fileTypes[fileType & 7], modifier);
screen.PrintText(true, x, y, bufferOut, textColour, bgColour);
y += 8;
}
entryOffset += 32;
}
}
else
{
// Error, just abort
complete = true;
}
}
}
x = 0;
//DEBUG_LOG("%d blocks free\r\n", blocksFree);
snprintf(bufferOut, 128, "%d BLOCKS FREE.\r\n", blocksFree);
screen.PrintText(true, x, y, bufferOut, textColour, bgColour);
y += 8;
}
DisplayStatusBar();
if (filenameForIcon)
{
FILINFO filIcon;
if (CheckForPNG(filenameForIcon, filIcon))
DisplayPNG(filIcon, 1024 - 320, 0);
}
}
void FileBrowser::AutoSelectTestImage()
{
FileBrowser::BrowsableList::Entry* current = 0;
int index;
int maxEntries = folder.entries.size();
for (index = 0; index < maxEntries; ++index)
{
current = &folder.entries[index];
if (strcmp(current->filImage.fname, "someimage.g64") == 0)
{
break;
}
}
if (index != maxEntries)
{
caddySelections.Clear();
caddySelections.entries.push_back(*current);
selectionsMade = FillCaddyWithSelections();
}
}

169
FileBrowser.h Normal file
View File

@ -0,0 +1,169 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#ifndef FileBrowser_H
#define FileBrowser_H
#include <assert.h>
#include "ff.h"
#include <vector>
#include "types.h"
#include "DiskImage.h"
#include "DiskCaddy.h"
#include "ROMs.h"
#define VIC2_COLOUR_INDEX_BLACK 0
#define VIC2_COLOUR_INDEX_WHITE 1
#define VIC2_COLOUR_INDEX_RED 2
#define VIC2_COLOUR_INDEX_CYAN 3
#define VIC2_COLOUR_INDEX_MAGENTA 4
#define VIC2_COLOUR_INDEX_GREEN 5
#define VIC2_COLOUR_INDEX_BLUE 6
#define VIC2_COLOUR_INDEX_YELLOW 7
#define VIC2_COLOUR_INDEX_ORANGE 8
#define VIC2_COLOUR_INDEX_BROWN 9
#define VIC2_COLOUR_INDEX_PINK 10
#define VIC2_COLOUR_INDEX_DGREY 11
#define VIC2_COLOUR_INDEX_GREY 12
#define VIC2_COLOUR_INDEX_LGREEN 13
#define VIC2_COLOUR_INDEX_LBLUE 14
#define VIC2_COLOUR_INDEX_LGREY 15
#define FILEBROWSER_MAX_PNG_SIZE 0x10000
#define STATUS_BAR_POSITION_Y (40 * 16 + 10)
class FileBrowser
{
public:
class BrowsableList
{
public:
BrowsableList()
: current(0)
, currentIndex(0)
, offset(0)
{
}
void Clear()
{
entries.clear();
current = 0;
currentIndex = 0;
offset = 0;
}
struct Entry
{
FILINFO filImage;
FILINFO filIcon;
};
Entry* FindEntry(const char* name);
std::vector<Entry> entries;
Entry* current;
u32 currentIndex;
u32 offset;
};
class Folder : public BrowsableList
{
public:
Folder()
: BrowsableList()
, name(0)
{
}
FILINFO* name;
};
FileBrowser(DiskCaddy* diskCaddy, ROMs* roms, unsigned deviceID, bool displayPNGIcons);
void AutoSelectTestImage();
void DisplayRoot();
void UpdateInput();
void RefeshDisplay();
void DisplayDiskInfo(DiskImage* diskImage, const char* filenameForIcon);
void DisplayStatusBar();
void FolderChanged();
void PopFolder();
bool SelectionsMade() { return selectionsMade; }
const char* LastSelectionName() { return lastSelectionName; }
void ClearSelections() { selectionsMade = false; caddySelections.Clear(); }
void ShowDeviceAndROM();
void ClearScreen();
void SetDeviceID(u8 id) { deviceID = id; }
Folder* GetCurrentFolder();
static const long int LSTBuffer_size = 1024 * 8;
static unsigned char LSTBuffer[];
static const unsigned SwapKeys[];
static u32 Colour(int index);
bool SelectLST(const char* filenameLST);
private:
void DisplayPNG(FILINFO& filIcon, int x, int y);
void RefreshFolderEntries();
void UpdateInputFolders();
void UpdateInputDiskCaddy();
void RefeshDisplayForBrowsableList(FileBrowser::BrowsableList* browsableList, int xOffset, bool showSelected = true);
bool FillCaddyWithSelections();
bool AddToCaddy(FileBrowser::BrowsableList::Entry* current);
bool CheckForPNG(const char* filename, FILINFO& filIcon);
void DisplayPNG();
enum State
{
State_Folders,
State_DiskCaddy
} state;
Folder folder;
u32 maxOnScreen;
DiskCaddy* diskCaddy;
bool selectionsMade;
const char* lastSelectionName;
ROMs* roms;
unsigned deviceID;
bool displayPNGIcons;
BrowsableList caddySelections;
char PNG[FILEBROWSER_MAX_PNG_SIZE];
};
#endif

49
IOPort.h Normal file
View File

@ -0,0 +1,49 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#ifndef IOPort_H
#define IOPort_H
#include <assert.h>
typedef void(*PortOutFn)(void*, unsigned char status);
class IOPort
{
public:
IOPort() : stateOut(0), stateIn(0), direction(0), portOutFn(0) {}
inline void SetInput(unsigned char pin, bool state)
{
if (state) stateIn |= pin;
else stateIn &= ~pin;
}
inline unsigned char GetInput() { return stateIn; }
inline void SetInput(unsigned char value) { stateIn = value; }
inline unsigned char GetOutput() { return stateOut; }
inline void SetOutput(unsigned char value) { stateOut = value; if (portOutFn) (portOutFn)(portOutFnThis, stateOut & direction); }
inline unsigned char GetDirection() { return direction; }
inline void SetDirection(unsigned char value) { direction = value; if (portOutFn) (portOutFn)(portOutFnThis, stateOut & direction); }
inline void SetPortOut(void* data, PortOutFn fn) { portOutFnThis = data; portOutFn = fn; }
private:
unsigned char stateOut;
unsigned char stateIn;
unsigned char direction;
PortOutFn portOutFn;
void* portOutFnThis;
};
#endif

195
InputMappings.cpp Normal file
View File

@ -0,0 +1,195 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#include "InputMappings.h"
#include "FileBrowser.h"
#include "iec_bus.h"
#include "debug.h"
extern "C"
{
#include "rpi-aux.h"
}
// If disk swaps can be done via multiple cores then directDiskSwapRequest needs to be volatile. WARNING: volatile acesses can be very expensive.
//volatile unsigned InputMappings::directDiskSwapRequest = 0;
unsigned InputMappings::directDiskSwapRequest = 0;
//volatile unsigned InputMappings::uartFlags = 0;
//unsigned InputMappings::escapeSequenceIndex = 0;
InputMappings::InputMappings()
{
}
bool InputMappings::CheckButtonsBrowseMode()
{
buttonFlags = 0;
if (IEC_Bus::GetInputButtonPressed(0))
SetButtonFlag(ENTER_FLAG);
else if (IEC_Bus::GetInputButtonRepeating(1))
SetButtonFlag(UP_FLAG);
else if (IEC_Bus::GetInputButtonRepeating(2))
SetButtonFlag(DOWN_FLAG);
else if (IEC_Bus::GetInputButtonPressed(3))
SetButtonFlag(BACK_FLAG);
else if (IEC_Bus::GetInputButtonPressed(4))
SetButtonFlag(INSERT_FLAG);
return buttonFlags != 0;
}
void InputMappings::CheckButtonsEmulationMode()
{
buttonFlags = 0;
if (IEC_Bus::GetInputButtonPressed(0))
SetButtonFlag(ESC_FLAG);
else if (IEC_Bus::GetInputButtonPressed(1))
SetButtonFlag(NEXT_FLAG);
else if (IEC_Bus::GetInputButtonPressed(2))
SetButtonFlag(PREV_FLAG);
//else if (IEC_Bus::GetInputButtonPressed(3))
// SetButtonFlag(BACK_FLAG);
}
//void InputMappings::CheckUart()
//{
// char charReceived;
//
// uartFlags = 0;
//
// if (RPI_AuxMiniUartRead(&charReceived))
// {
// DEBUG_LOG("charReceived=%c %02x\r\n", charReceived, charReceived);
// if (charReceived == '[')
// {
// escapeSequenceIndex++;
// }
// else
// {
// if (escapeSequenceIndex == 0)
// {
// if (charReceived == 27)
// SetUartFlag(ESC_FLAG);
// else if (charReceived == 13)
// SetUartFlag(ENTER_FLAG);
// else if (charReceived == ' ')
// SetUartFlag(SPACE_FLAG);
// else if (charReceived == 0x7f)
// SetUartFlag(BACK_FLAG);
// //else if (charReceived == 'u')
// // SetUartFlag(UP_FLAG);
// //else if (charReceived == 'U')
// // SetUartFlag(PAGEUP_FLAG);
// //else if (charReceived == 'd')
// // SetUartFlag(DOWN_FLAG);
// //else if (charReceived == 'D')
// // SetUartFlag(PAGEDOWN_FLAG);
// else
// {
// char number = charReceived - '0';
// if (number >= 0 && number <= 9)
// {
// if (number == 0)
// number = 10;
// directDiskSwapRequest |= (1 << (number - 1));
// printf("SWAP %d\r\n", number);
// }
// }
// }
// else if (escapeSequenceIndex == 1)
// {
// if (charReceived == 'A')
// SetUartFlag(UP_FLAG);
// else if (charReceived == 'B')
// SetUartFlag(DOWN_FLAG);
// else if (charReceived == 'C')
// SetUartFlag(PAGEDOWN_FLAG);
// else if (charReceived == 'D')
// SetUartFlag(PAGEUP_FLAG);
// else if (charReceived == '2')
// SetUartFlag(INSERT_FLAG);
// escapeSequenceIndex = 0;
// }
// }
// }
//}
bool InputMappings::CheckKeyboardBrowseMode()
{
Keyboard* keyboard = Keyboard::Instance();
keyboardFlags = 0;
if (keyboard->KeyHeld(KEY_ESC))
SetKeyboardFlag(ESC_FLAG);
else if (keyboard->KeyHeld(KEY_ENTER))
SetKeyboardFlag(ENTER_FLAG);
else if (keyboard->KeyHeld(KEY_BACKSPACE))
SetKeyboardFlag(BACK_FLAG);
else if (keyboard->KeyHeld(KEY_SPACE))
SetKeyboardFlag(SPACE_FLAG);
else if (keyboard->KeyHeld(KEY_INSERT))
SetKeyboardFlag(INSERT_FLAG);
else if (keyboard->KeyHeld(KEY_UP))
SetKeyboardFlag(UP_FLAG);
else if (keyboard->KeyHeld(KEY_PAGEUP) || keyboard->KeyHeld(KEY_LEFT))
SetKeyboardFlag(PAGEUP_FLAG);
else if (keyboard->KeyHeld(KEY_DOWN))
SetKeyboardFlag(DOWN_FLAG);
else if (keyboard->KeyHeld(KEY_PAGEDOWN) || keyboard->KeyHeld(KEY_RIGHT))
SetKeyboardFlag(PAGEDOWN_FLAG);
else
{
unsigned index;
for (index = 0; index < 10; ++index)
{
unsigned keySetIndexBase = index * 3;
if (keyboard->KeyHeld(FileBrowser::SwapKeys[keySetIndexBase]) || keyboard->KeyHeld(FileBrowser::SwapKeys[keySetIndexBase + 1]) || keyboard->KeyHeld(FileBrowser::SwapKeys[keySetIndexBase + 2]))
keyboardFlags |= NUMBER_FLAG;
}
}
return keyboardFlags != 0;
}
void InputMappings::CheckKeyboardEmulationMode(unsigned numberOfImages, unsigned numberOfImagesMax)
{
Keyboard* keyboard = Keyboard::Instance();
keyboardFlags = 0;
if (keyboard->CheckChanged())
{
if (keyboard->KeyHeld(KEY_ESC))
SetKeyboardFlag(ESC_FLAG);
else if (keyboard->KeyHeld(KEY_PAGEUP))
SetKeyboardFlag(PREV_FLAG);
else if (keyboard->KeyHeld(KEY_PAGEDOWN))
SetKeyboardFlag(NEXT_FLAG);
else if (numberOfImages > 1)
{
unsigned index;
for (index = 0; index < numberOfImagesMax; ++index)
{
unsigned keySetIndexBase = index * 3;
if (keyboard->KeyHeld(FileBrowser::SwapKeys[keySetIndexBase]) || keyboard->KeyHeld(FileBrowser::SwapKeys[keySetIndexBase + 1]) || keyboard->KeyHeld(FileBrowser::SwapKeys[keySetIndexBase + 2]))
directDiskSwapRequest |= (1 << index);
}
}
}
}

129
InputMappings.h Normal file
View File

@ -0,0 +1,129 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#ifndef InputMappings_H
#define InputMappings_H
#include "Singleton.h"
#include "Keyboard.h"
#define ESC_FLAG (1 << 0)
#define NEXT_FLAG (1 << 1)
#define PREV_FLAG (1 << 2)
#define ENTER_FLAG (1 << 3)
#define UP_FLAG (1 << 4)
#define PAGEUP_FLAG (1 << 5)
#define DOWN_FLAG (1 << 6)
#define PAGEDOWN_FLAG (1 << 7)
#define SPACE_FLAG (1 << 8)
#define BACK_FLAG (1 << 9)
#define INSERT_FLAG (1 << 10)
#define NUMBER_FLAG (1 << 11)
class InputMappings : public Singleton<InputMappings>
{
protected:
friend Singleton<InputMappings>;
unsigned keyboardFlags;
unsigned buttonFlags;
//inline void SetUartFlag(unsigned flag) { uartFlags |= flag; }
//inline bool UartFlag(unsigned flag) { return (uartFlags & flag) != 0; }
inline void SetKeyboardFlag(unsigned flag) { keyboardFlags |= flag; }
inline bool KeyboardFlag(unsigned flag) { return (keyboardFlags & flag) != 0; }
inline void SetButtonFlag(unsigned flag) { buttonFlags |= flag; }
inline bool ButtonFlag(unsigned flag) { return (buttonFlags & flag) != 0; }
public:
InputMappings();
//void CheckUart(); // One core will call this
bool CheckKeyboardBrowseMode();
void CheckKeyboardEmulationMode(unsigned numberOfImages, unsigned numberOfImagesMax); // The other core will call this
bool CheckButtonsBrowseMode();
void CheckButtonsEmulationMode();
void Reset()
{
keyboardFlags = 0;
buttonFlags = 0;
}
inline bool Exit()
{
return KeyboardFlag(ESC_FLAG)/* | UartFlag(ESC_FLAG)*/ | ButtonFlag(ESC_FLAG);
}
inline bool NextDisk()
{
return KeyboardFlag(NEXT_FLAG)/* | UartFlag(NEXT_FLAG)*/ | ButtonFlag(NEXT_FLAG);
}
inline bool PrevDisk()
{
return KeyboardFlag(PREV_FLAG)/* | UartFlag(PREV_FLAG)*/ | ButtonFlag(PREV_FLAG);
}
inline bool BrowseSelect()
{
return KeyboardFlag(ENTER_FLAG)/* | UartFlag(ENTER_FLAG)*/ | ButtonFlag(ENTER_FLAG);
}
inline bool BrowseDone()
{
return KeyboardFlag(SPACE_FLAG)/* | UartFlag(SPACE_FLAG)*/;
}
inline bool BrowseBack()
{
return KeyboardFlag(BACK_FLAG)/* | UartFlag(BACK_FLAG)*/ | ButtonFlag(BACK_FLAG);
}
inline bool BrowseUp()
{
return KeyboardFlag(UP_FLAG)/* | UartFlag(UP_FLAG)*/ | ButtonFlag(UP_FLAG);
}
inline bool BrowsePageUp()
{
return KeyboardFlag(PAGEUP_FLAG)/* | UartFlag(PAGEUP_FLAG)*/;
}
inline bool BrowseDown()
{
return KeyboardFlag(DOWN_FLAG)/* | UartFlag(DOWN_FLAG)*/ | ButtonFlag(DOWN_FLAG);
}
inline bool BrowsePageDown()
{
return KeyboardFlag(PAGEDOWN_FLAG)/* | UartFlag(PAGEDOWN_FLAG)*/;
}
inline bool BrowseInsert()
{
return KeyboardFlag(INSERT_FLAG)/* | UartFlag(INSERT_FLAG)*/ | ButtonFlag(INSERT_FLAG);
}
// Used by the 2 cores so need to be volatile
//volatile static unsigned directDiskSwapRequest;
static unsigned directDiskSwapRequest;
//volatile static unsigned uartFlags; // WARNING uncached volatile accessed across cores can be very expensive and may cause the emulation to exceed the 1us time frame and a realtime cycle will not be emulated correctly!
//private:
// static unsigned escapeSequenceIndex;
};
#endif

155
Keyboard.cpp Normal file
View File

@ -0,0 +1,155 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#include "Keyboard.h"
#include <string.h>
#include <uspi.h>
#include "timer.h"
#include "debug.h"
extern "C"
{
#include "uspi\devicenameservice.h"
}
#define REPEAT_RATE 8
#define REPEAT_DELAY 3
void Keyboard::KeyPressedHandlerRaw(TUSBKeyboardDevice* device, unsigned char modifiers, const unsigned char RawKeys[6])
{
// byte 0 - modifires
// bit 0: left control
// bit 1 : left shift
// bit 2 : left alt
// bit 3 : left GUI(Win / Apple / Meta key)
// bit 4 : right control
// bit 5 : right shift
// bit 6 : right alt
// bit 7 : right GUI
// byte 1 - reserved
// bytes 2-7 - represent the keys that are concurrently pressed (up to 6 in this case)
// ranges from 0x04 to 0xE7
// If no keys are currently pressed then all 6 bytes should contain 0x00
// The key repeat delay and rate is purely a host function.
//DEBUG_LOG("KeyPressedHandlerRaw: %x %d %d %d %d %d %d\r\n", modifiers, RawKeys[0], RawKeys[1], RawKeys[2], RawKeys[3], RawKeys[4], RawKeys[5]);
Keyboard* keyboard = Keyboard::Instance();
bool anyDown = false;
keyboard->keyStatusPrev[0] = keyboard->keyStatus[0];
keyboard->keyStatusPrev[1] = keyboard->keyStatus[1];
keyboard->keyStatus[0] = 0;
keyboard->keyStatus[1] = 0;
keyboard->modifier = modifiers;
int index;
for (index = 0; index < 6; ++index)
{
u8 rawKey = RawKeys[index];
if (rawKey >= 4)
{
int keyStatusIndex = (rawKey >= 64) ? 1 : 0;
//DEBUG_LOG("%x %d\r\n", rawKey, keyStatusIndex);
u64 keyBit = 1ULL << (u64)(rawKey & 0x3f);
if (keyboard->keyStatusPrev[keyStatusIndex] & keyBit)
{
}
else
{
keyboard->keyRepeatCount[rawKey] = 0;
}
keyboard->keyStatus[keyStatusIndex] |= keyBit;
anyDown = true;
}
}
if (anyDown)
{
// Only need the timer if a key was held down
if (keyboard->timer == 0)
{
keyboard->timer = TimerStartKernelTimer(REPEAT_RATE, USBKeyboardDeviceTimerHandler, 0, device);
//DEBUG_LOG("Timer started\r\n");
}
}
else
{
if (keyboard->timer != 0)
{
TimerCancelKernelTimer(keyboard->timer);
keyboard->timer = 0;
}
}
keyboard->updateCount++;
}
void Keyboard::USBKeyboardDeviceTimerHandler(unsigned hTimer, void *pParam, void *pContext)
{
Keyboard* keyboard = Keyboard::Instance();
bool anyDown = false;
int keyStatusIndex;
int keyIndex;
for (keyStatusIndex = 0; keyStatusIndex < 2; ++keyStatusIndex)
{
for (keyIndex = 0; keyIndex < 64; ++keyIndex)
{
int keyCodeIndex = keyStatusIndex * 64 + keyIndex;
if (keyboard->keyStatus[keyStatusIndex] & (1ULL << keyIndex))
{
anyDown = true;
keyboard->keyRepeatCount[keyCodeIndex]++;
if (keyboard->keyRepeatCount[keyCodeIndex] > REPEAT_DELAY)
keyboard->updateCount++;
}
else
{
keyboard->keyRepeatCount[keyCodeIndex] = 0;
}
}
}
keyboard->keyStatusPrev[0] = keyboard->keyStatus[0];
keyboard->keyStatusPrev[1] = keyboard->keyStatus[1];
if (keyboard->timer != 0)
{
TimerCancelKernelTimer(keyboard->timer);
keyboard->timer = 0;
}
if (anyDown) // Only need the timer if a key was held down
keyboard->timer = TimerStartKernelTimer(REPEAT_RATE, USBKeyboardDeviceTimerHandler, 0, pContext);
}
Keyboard::Keyboard()
: modifier(0)
, timer(0)
, updateCount(0)
, updateCountLastRead(-1)
{
keyStatus[0] = 0;
keyStatus[1] = 0;
memset(keyRepeatCount, 0, sizeof(keyRepeatCount));
USPiKeyboardRegisterKeyStatusHandlerRaw(KeyPressedHandlerRaw);
}

348
Keyboard.h Normal file
View File

@ -0,0 +1,348 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#ifndef Keyboard_H
#define Keyboard_H
#include "Singleton.h"
#include <stdio.h>
extern "C"
{
#include <uspi\usbkeyboard.h>
}
#define MAX_KEYS 0x7f
#define KEY_MOD_LCTRL 0x01
#define KEY_MOD_LSHIFT 0x02
#define KEY_MOD_LALT 0x04
#define KEY_MOD_LMETA 0x08
#define KEY_MOD_RCTRL 0x10
#define KEY_MOD_RSHIFT 0x20
#define KEY_MOD_RALT 0x40
#define KEY_MOD_RMETA 0x80
#define KEY_A 0x04 // Keyboard a and A
#define KEY_B 0x05 // Keyboard b and B
#define KEY_C 0x06 // Keyboard c and C
#define KEY_D 0x07 // Keyboard d and D
#define KEY_E 0x08 // Keyboard e and E
#define KEY_F 0x09 // Keyboard f and F
#define KEY_G 0x0a // Keyboard g and G
#define KEY_H 0x0b // Keyboard h and H
#define KEY_I 0x0c // Keyboard i and I
#define KEY_J 0x0d // Keyboard j and J
#define KEY_K 0x0e // Keyboard k and K
#define KEY_L 0x0f // Keyboard l and L
#define KEY_M 0x10 // Keyboard m and M
#define KEY_N 0x11 // Keyboard n and N
#define KEY_O 0x12 // Keyboard o and O
#define KEY_P 0x13 // Keyboard p and P
#define KEY_Q 0x14 // Keyboard q and Q
#define KEY_R 0x15 // Keyboard r and R
#define KEY_S 0x16 // Keyboard s and S
#define KEY_T 0x17 // Keyboard t and T
#define KEY_U 0x18 // Keyboard u and U
#define KEY_V 0x19 // Keyboard v and V
#define KEY_W 0x1a // Keyboard w and W
#define KEY_X 0x1b // Keyboard x and X
#define KEY_Y 0x1c // Keyboard y and Y
#define KEY_Z 0x1d // Keyboard z and Z
#define KEY_1 0x1e // Keyboard 1 and !
#define KEY_2 0x1f // Keyboard 2 and @
#define KEY_3 0x20 // Keyboard 3 and #
#define KEY_4 0x21 // Keyboard 4 and $
#define KEY_5 0x22 // Keyboard 5 and %
#define KEY_6 0x23 // Keyboard 6 and ^
#define KEY_7 0x24 // Keyboard 7 and &
#define KEY_8 0x25 // Keyboard 8 and *
#define KEY_9 0x26 // Keyboard 9 and (
#define KEY_0 0x27 // Keyboard 0 and )
#define KEY_ENTER 0x28 // Keyboard Return (ENTER)
#define KEY_ESC 0x29 // Keyboard ESCAPE
#define KEY_BACKSPACE 0x2a // Keyboard DELETE (Backspace)
#define KEY_TAB 0x2b // Keyboard Tab
#define KEY_SPACE 0x2c // Keyboard Spacebar
#define KEY_MINUS 0x2d // Keyboard - and _
#define KEY_EQUAL 0x2e // Keyboard = and +
#define KEY_LEFTBRACE 0x2f // Keyboard [ and {
#define KEY_RIGHTBRACE 0x30 // Keyboard ] and }
#define KEY_BACKSLASH 0x31 // Keyboard \ and |
#define KEY_HASHTILDE 0x32 // Keyboard Non-US # and ~
#define KEY_SEMICOLON 0x33 // Keyboard ; and :
#define KEY_APOSTROPHE 0x34 // Keyboard ' and "
#define KEY_GRAVE 0x35 // Keyboard ` and ~
#define KEY_COMMA 0x36 // Keyboard , and <
#define KEY_DOT 0x37 // Keyboard . and >
#define KEY_SLASH 0x38 // Keyboard / and ?
#define KEY_CAPSLOCK 0x39 // Keyboard Caps Lock
#define KEY_F1 0x3a // Keyboard F1
#define KEY_F2 0x3b // Keyboard F2
#define KEY_F3 0x3c // Keyboard F3
#define KEY_F4 0x3d // Keyboard F4
#define KEY_F5 0x3e // Keyboard F5
#define KEY_F6 0x3f // Keyboard F6
#define KEY_F7 0x40 // Keyboard F7
#define KEY_F8 0x41 // Keyboard F8
#define KEY_F9 0x42 // Keyboard F9
#define KEY_F10 0x43 // Keyboard F10
#define KEY_F11 0x44 // Keyboard F11
#define KEY_F12 0x45 // Keyboard F12
#define KEY_SYSRQ 0x46 // Keyboard Print Screen
#define KEY_SCROLLLOCK 0x47 // Keyboard Scroll Lock
#define KEY_PAUSE 0x48 // Keyboard Pause
#define KEY_INSERT 0x49 // Keyboard Insert
#define KEY_HOME 0x4a // Keyboard Home
#define KEY_PAGEUP 0x4b // Keyboard Page Up
#define KEY_DELETE 0x4c // Keyboard Delete Forward
#define KEY_END 0x4d // Keyboard End
#define KEY_PAGEDOWN 0x4e // Keyboard Page Down
#define KEY_RIGHT 0x4f // Keyboard Right Arrow
#define KEY_LEFT 0x50 // Keyboard Left Arrow
#define KEY_DOWN 0x51 // Keyboard Down Arrow
#define KEY_UP 0x52 // Keyboard Up Arrow
#define KEY_NUMLOCK 0x53 // Keyboard Num Lock and Clear
#define KEY_KPSLASH 0x54 // Keypad /
#define KEY_KPASTERISK 0x55 // Keypad *
#define KEY_KPMINUS 0x56 // Keypad -
#define KEY_KPPLUS 0x57 // Keypad +
#define KEY_KPENTER 0x58 // Keypad ENTER
#define KEY_KP1 0x59 // Keypad 1 and End
#define KEY_KP2 0x5a // Keypad 2 and Down Arrow
#define KEY_KP3 0x5b // Keypad 3 and PageDn
#define KEY_KP4 0x5c // Keypad 4 and Left Arrow
#define KEY_KP5 0x5d // Keypad 5
#define KEY_KP6 0x5e // Keypad 6 and Right Arrow
#define KEY_KP7 0x5f // Keypad 7 and Home
#define KEY_KP8 0x60 // Keypad 8 and Up Arrow
#define KEY_KP9 0x61 // Keypad 9 and Page Up
#define KEY_KP0 0x62 // Keypad 0 and Insert
#define KEY_KPDOT 0x63 // Keypad . and Delete
#define KEY_102ND 0x64 // Keyboard Non-US \ and |
#define KEY_COMPOSE 0x65 // Keyboard Application
#define KEY_POWER 0x66 // Keyboard Power
#define KEY_KPEQUAL 0x67 // Keypad =
#define KEY_F13 0x68 // Keyboard F13
#define KEY_F14 0x69 // Keyboard F14
#define KEY_F15 0x6a // Keyboard F15
#define KEY_F16 0x6b // Keyboard F16
#define KEY_F17 0x6c // Keyboard F17
#define KEY_F18 0x6d // Keyboard F18
#define KEY_F19 0x6e // Keyboard F19
#define KEY_F20 0x6f // Keyboard F20
#define KEY_F21 0x70 // Keyboard F21
#define KEY_F22 0x71 // Keyboard F22
#define KEY_F23 0x72 // Keyboard F23
#define KEY_F24 0x73 // Keyboard F24
#define KEY_OPEN 0x74 // Keyboard Execute
#define KEY_HELP 0x75 // Keyboard Help
#define KEY_PROPS 0x76 // Keyboard Menu
#define KEY_FRONT 0x77 // Keyboard Select
#define KEY_STOP 0x78 // Keyboard Stop
#define KEY_AGAIN 0x79 // Keyboard Again
#define KEY_UNDO 0x7a // Keyboard Undo
#define KEY_CUT 0x7b // Keyboard Cut
#define KEY_COPY 0x7c // Keyboard Copy
#define KEY_PASTE 0x7d // Keyboard Paste
#define KEY_FIND 0x7e // Keyboard Find
#define KEY_MUTE 0x7f // Keyboard Mute
#define KEY_VOLUMEUP 0x80 // Keyboard Volume Up
#define KEY_VOLUMEDOWN 0x81 // Keyboard Volume Down
// 0x82 Keyboard Locking Caps Lock
// 0x83 Keyboard Locking Num Lock
// 0x84 Keyboard Locking Scroll Lock
#define KEY_KPCOMMA 0x85 // Keypad Comma
// 0x86 Keypad Equal Sign
#define KEY_RO 0x87 // Keyboard International1
#define KEY_KATAKANAHIRAGANA 0x88 // Keyboard International2
#define KEY_YEN 0x89 // Keyboard International3
#define KEY_HENKAN 0x8a // Keyboard International4
#define KEY_MUHENKAN 0x8b // Keyboard International5
#define KEY_KPJPCOMMA 0x8c // Keyboard International6
// 0x8d Keyboard International7
// 0x8e Keyboard International8
// 0x8f Keyboard International9
#define KEY_HANGEUL 0x90 // Keyboard LANG1
#define KEY_HANJA 0x91 // Keyboard LANG2
#define KEY_KATAKANA 0x92 // Keyboard LANG3
#define KEY_HIRAGANA 0x93 // Keyboard LANG4
#define KEY_ZENKAKUHANKAKU 0x94 // Keyboard LANG5
// 0x95 Keyboard LANG6
// 0x96 Keyboard LANG7
// 0x97 Keyboard LANG8
// 0x98 Keyboard LANG9
// 0x99 Keyboard Alternate Erase
// 0x9a Keyboard SysReq/Attention
// 0x9b Keyboard Cancel
// 0x9c Keyboard Clear
// 0x9d Keyboard Prior
// 0x9e Keyboard Return
// 0x9f Keyboard Separator
// 0xa0 Keyboard Out
// 0xa1 Keyboard Oper
// 0xa2 Keyboard Clear/Again
// 0xa3 Keyboard CrSel/Props
// 0xa4 Keyboard ExSel
// 0xb0 Keypad 00
// 0xb1 Keypad 000
// 0xb2 Thousands Separator
// 0xb3 Decimal Separator
// 0xb4 Currency Unit
// 0xb5 Currency Sub-unit
#define KEY_KPLEFTPAREN 0xb6 // Keypad (
#define KEY_KPRIGHTPAREN 0xb7 // Keypad )
// 0xb8 Keypad {
// 0xb9 Keypad }
// 0xba Keypad Tab
// 0xbb Keypad Backspace
// 0xbc Keypad A
// 0xbd Keypad B
// 0xbe Keypad C
// 0xbf Keypad D
// 0xc0 Keypad E
// 0xc1 Keypad F
// 0xc2 Keypad XOR
// 0xc3 Keypad ^
// 0xc4 Keypad %
// 0xc5 Keypad <
// 0xc6 Keypad >
// 0xc7 Keypad &
// 0xc8 Keypad &&
// 0xc9 Keypad |
// 0xca Keypad ||
// 0xcb Keypad :
// 0xcc Keypad #
// 0xcd Keypad Space
// 0xce Keypad @
// 0xcf Keypad !
// 0xd0 Keypad Memory Store
// 0xd1 Keypad Memory Recall
// 0xd2 Keypad Memory Clear
// 0xd3 Keypad Memory Add
// 0xd4 Keypad Memory Subtract
// 0xd5 Keypad Memory Multiply
// 0xd6 Keypad Memory Divide
// 0xd7 Keypad +/-
// 0xd8 Keypad Clear
// 0xd9 Keypad Clear Entry
// 0xda Keypad Binary
// 0xdb Keypad Octal
// 0xdc Keypad Decimal
// 0xdd Keypad Hexadecimal
#define KEY_LEFTCTRL 0xe0 // Keyboard Left Control
#define KEY_LEFTSHIFT 0xe1 // Keyboard Left Shift
#define KEY_LEFTALT 0xe2 // Keyboard Left Alt
#define KEY_LEFTMETA 0xe3 // Keyboard Left GUI
#define KEY_RIGHTCTRL 0xe4 // Keyboard Right Control
#define KEY_RIGHTSHIFT 0xe5 // Keyboard Right Shift
#define KEY_RIGHTALT 0xe6 // Keyboard Right Alt
#define KEY_RIGHTMETA 0xe7 // Keyboard Right GUI
#define KEY_MEDIA_PLAYPAUSE 0xe8
#define KEY_MEDIA_STOPCD 0xe9
#define KEY_MEDIA_PREVIOUSSONG 0xea
#define KEY_MEDIA_NEXTSONG 0xeb
#define KEY_MEDIA_EJECTCD 0xec
#define KEY_MEDIA_VOLUMEUP 0xed
#define KEY_MEDIA_VOLUMEDOWN 0xee
#define KEY_MEDIA_MUTE 0xef
#define KEY_MEDIA_WWW 0xf0
#define KEY_MEDIA_BACK 0xf1
#define KEY_MEDIA_FORWARD 0xf2
#define KEY_MEDIA_STOP 0xf3
#define KEY_MEDIA_FIND 0xf4
#define KEY_MEDIA_SCROLLUP 0xf5
#define KEY_MEDIA_SCROLLDOWN 0xf6
#define KEY_MEDIA_EDIT 0xf7
#define KEY_MEDIA_SLEEP 0xf8
#define KEY_MEDIA_COFFEE 0xf9
#define KEY_MEDIA_REFRESH 0xfa
#define KEY_MEDIA_CALC 0xfb
class Keyboard : public Singleton<Keyboard>
{
protected:
friend Singleton<Keyboard>;
u8 modifier;
volatile u64 keyStatus[2];
volatile u64 keyStatusPrev[2];
u32 keyRepeatCount[MAX_KEYS];
u32 timer;
//volatile bool dirty;
volatile u32 updateCount;
u32 updateCountLastRead;
static void KeyPressedHandlerRaw(TUSBKeyboardDevice* device, unsigned char modifiers, const unsigned char RawKeys[6]);
static void USBKeyboardDeviceTimerHandler(unsigned hTimer, void *pParam, void *pContext);
public:
Keyboard();
//inline u32 UpdateCount() const { return updateCount; }
inline bool CheckChanged()
{
if (updateCountLastRead == updateCount)
{
return false;
}
else
{
updateCountLastRead = updateCount;
return true;
}
// bool dirtyOld = dirty;
// dirty = false;
// return dirtyOld;
}
inline bool KeyPressed(u32 rawKey)
{
int keyStatusIndex = rawKey >= 64 ? 1 : 0;
u64 mask = 1ULL << (rawKey & 0x3f);
return ((keyStatus[keyStatusIndex] & mask) && !(keyStatusPrev[keyStatusIndex] & mask));
}
inline bool KeyReleased(u32 rawKey)
{
int keyStatusIndex = rawKey >= 64 ? 1 : 0;
u64 mask = 1ULL << (rawKey & 0x3f);
return (!(keyStatus[keyStatusIndex] & mask) && (keyStatusPrev[keyStatusIndex] & mask));
}
inline bool KeyHeld(u32 rawKey)
{
int keyStatusIndex = rawKey >= 64 ? 1 : 0;
u64 mask = 1ULL << (rawKey & 0x3f);
return (keyStatus[keyStatusIndex] & mask);
}
inline bool KeyAnyHeld()
{
return (keyStatus[0] | keyStatus[1]);
}
};
#endif

81
Makefile Normal file
View File

@ -0,0 +1,81 @@
#
# Makefile
#
OBJS = armc-start.o armc-cstartup.o armc-cstubs.o armc-cppstubs.o exception.o main.o rpi-aux.o rpi-mailbox-interface.o rpi-mailbox.o rpi-gpio.o rpi-interrupts.o cache.o ff.o interrupt.o keyboard.o Pi1541.o DiskImage.o iec_bus.o iec_commands.o m6502.o m6522.o drive.o gcr.o prot.o lz.o emmc.o diskio.o options.o Screen.o Timer.o FileBrowser.o DiskCaddy.o ROMs.o InputMappings.o xga_font_data.o
kernel.img: $(OBJS) $(OBJSS)
$(LD) -o kernel.elf -Map kernel.map -T linker.ld $(OBJS) $(LIBS)
$(PREFIX)objdump -d kernel.elf | $(PREFIX)c++filt > kernel.lst
$(PREFIX)objcopy kernel.elf -O binary kernel.img
wc -c kernel.img
GCC_BASE = "C:/Program Files (x86)/GNU Tools ARM Embedded/5.4 2016q2"
RASPPI ?= 3
PREFIX ?= arm-none-eabi-
CC = $(PREFIX)gcc
CPP = $(PREFIX)g++
AS = $(CC)
LD = $(PREFIX)ld
AR = $(PREFIX)ar
ifeq ($(strip $(RASPPI)),0)
#ARCH ?= -march=armv6zk -mtune=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard -DRPIZERO=1 -DDEBUG
ARCH ?= -march=armv6zk -mtune=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard -DRPIZERO=1
CFLAGS += -DRPIZERO=1
endif
ifeq ($(strip $(RASPPI)),1)
ARCH ?= -march=armv6zk -mtune=arm1176jzf-s -mfloat-abi=hard -DRPIZERO=1
CFLAGS += -DRPIBPLUS=1
endif
ifeq ($(strip $(RASPPI)),2)
ARCH ?= -march=armv7-a -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard -marm -DRPI2=1
CFLAGS += -DRPI2=1
endif
ifeq ($(strip $(RASPPI)),3)
ARCH ?= -march=armv8-a -mtune=cortex-a53 -mfpu=neon-fp-armv8 -mfloat-abi=hard -marm -DRPI3=1 -DDEBUG -DNDEBUG
#ARCH ?= -march=armv8-a -mtune=cortex-a53 -mfpu=neon-fp-armv8 -mfloat-abi=hard -marm -DRPI3=1
CFLAGS += -DRPI3=1
endif
LIBS = $(GCC_BASE)/arm-none-eabi/lib/fpu/libc.a $(GCC_BASE)/lib/gcc/arm-none-eabi/5.4.1/fpu/libgcc.a
LIBS += uspi/lib/libuspi.a -lstdc++
INCLUDE += -I $(GCC_BASE)/arm-none-eabi/include -I $(GCC_BASE)/lib/gcc/arm-none-eabi/5.4.1/include
#INCLUDE += -I USB/include
INCLUDE += -I uspi/include
#INCLUDE += -I uspi/include/uspi
AFLAGS += $(ARCH) $(INCLUDE)
CFLAGS += $(ARCH) -Wall -Wno-psabi -fsigned-char -fno-builtin $(INCLUDE)
#-Wno-packed-bitfield-compat
#CFLAGS += -O3
#CFLAGS += -O4
CFLAGS += -Ofast
CPPFLAGS+= $(CFLAGS) -fno-exceptions -fno-rtti -std=c++0x -Wno-write-strings
CFLAGS += -fno-delete-null-pointer-checks -fdata-sections -ffunction-sections -u _printf_float
%.o: %.S
$(AS) $(AFLAGS) -c -o $@ $<
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
#assm add -S
%.o: %.cpp
$(CPP) $(CPPFLAGS) -c -o $@ $<
clean:
# rm -f *.o *.a *.elf *.lst *.img *.cir *.map *~ $(EXTRACLEAN)
del *.o
del *.a
del *.elf
del *.img
#arm-none-eabi-objdump -D kernel.elf > kernel.elf.asm

54
Petscii.h Normal file
View File

@ -0,0 +1,54 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#ifndef PETSCII
#define PETSCII
static inline u8 ascii2petscii(u8 ch)
{
if (ch > 64 && ch < 91) ch += 128;
else if (ch > 96 && ch < 123) ch -= 32;
else if (ch > 192 && ch < 219) ch -= 128;
return ch;
}
static inline u8 petscii2ascii(u8 ch)
{
if (ch >(64 + 128) && ch < (91 + 128)) ch -= 128;
else if (ch >(96 - 32) && ch < (123 - 32)) ch += 32;
else if (ch >(192 - 128) && ch < (219 - 128)) ch += 128;
return ch;
}
static inline u8 petscii2screen(u8 ch)
{
if ((ch >= 0x40 && ch <= 0x5F) || (ch >= 0xa0 && ch <= 0xbf)) ch -= 0x40;
else if (ch >= 0xc0 && ch <= 0xdf) ch -= 0x80;
else if (ch >= 0 && ch <= 0x1f) ch += 0x80;
else if ((ch >= 0x60 && ch <= 0x7F) || (ch >= 0x90 && ch <= 0x9f)) ch += 0x40;
return ch;
}
static inline u8 screen2petscii(u8 ch)
{
if ((ch >= 0 && ch <= 0x1F) || (ch >= 0x60 && ch <= 0x7f)) ch += 0x40;
else if (ch >= 0x40 && ch <= 0x5f) ch += 0x80;
else if (ch >= 0x80 && ch <= 0x9f) ch -= 0x80;
else if ((ch >= 0xa0 && ch <= 0xbF) || (ch >= 0xd0 && ch <= 0xdf)) ch -= 0x40;
return ch;
}
#endif

77
Pi1541.cpp Normal file
View File

@ -0,0 +1,77 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#include "Pi1541.h"
#include "debug.h"
Pi1541::Pi1541()
{
VIA[0].ConnectIRQ(&m6502.IRQ);
VIA[1].ConnectIRQ(&m6502.IRQ);
}
void Pi1541::Initialise()
{
VIA[0].ConnectIRQ(&m6502.IRQ);
VIA[1].ConnectIRQ(&m6502.IRQ);
}
//void Pi1541::ConfigureOfExtraRAM(bool extraRAM)
//{
// if (extraRAM)
// m6502.SetBusFunctions(this, Read6502ExtraRAM, Write6502ExtraRAM);
// else
// m6502.SetBusFunctions(this, Read6502, Write6502);
//}
void Pi1541::Update()
{
if (drive.Update())
{
//This pin sets the overflow flag on a negative transition from TTL one to TTL zero.
// SO is sampled at the trailing edge of P1, the cpu V flag is updated at next P1.
m6502.SO();
}
VIA[1].Execute();
VIA[0].Execute();
}
void Pi1541::Reset()
{
IOPort* VIABortB;
// Must reset the VIAs first as the devices will initialise inputs (eg CA1 ports etc)
// - VIAs will reset the inputs to a default value
// - devices will then set the inputs
// - could cause a IR_CXX IRQ
// - possibilities
// - reset while an ATN
// - reset while !BYTE SYNC
// - should be fine as VIA's functionControlRegister is reset to 0 and IRQs will be turned off
VIA[0].Reset();
VIA[1].Reset();
drive.Reset();
IEC_Bus::Reset();
// On a real drive the outputs look like they are being pulled high (when set to inputs) (Taking an input from the front end of an inverter)
VIABortB = VIA[0].GetPortB();
VIABortB->SetInput(VIAPORTPINS_DATAOUT, true);
VIABortB->SetInput(VIAPORTPINS_CLOCKOUT, true);
VIABortB->SetInput(VIAPORTPINS_ATNAOUT, true);
}

55
Pi1541.h Normal file
View File

@ -0,0 +1,55 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#ifndef PI1541_H
#define PI1541_H
#include "Drive.h"
#include "m6502.h"
#include "iec_bus.h"
class Pi1541
{
public:
Pi1541();
void Initialise();
void Update();
void Reset();
//void ConfigureOfExtraRAM(bool extraRAM);
Drive drive;
m6522 VIA[2];
M6502 m6502;
private:
//u8 Memory[0xc000];
//static u8 Read6502(u16 address, void* data);
//static u8 Read6502ExtraRAM(u16 address, void* data);
//static u8 Peek6502(u16 address, void* data);
//static void Write6502(u16 address, const u8 value, void* data);
//static void Write6502ExtraRAM(u16 address, const u8 value, void* data);
};
#endif

View File

@ -1,2 +1,29 @@
# Pi1541
Commodore 1541 emulator for the Raspberry Pi
Pi1541 is a real-time, cycle exact, Commodore 1541 disk drive emulator that can run on a Raspberry Pi 3B (or 3B+). The software is free and I have endeavored to make the hardware as simple and inexpensive as possible.
Pi1541 provides you with an SD card solution for using D64, G64, NIB and NBZ Commodore disk images on real Commodore 8 bit computers such as;-
Commodore 64
Commodore 128
Commodore Vic20
Commodore 16
Commodore Plus4
See www.pi1541.com for SD card and hardware configurations.
Building
--------
I use GNU Tools ARM Embedded tool chain 5.4.1 on Windows using make. https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads/5-2016-q2-update
There are two make files.
One in uspi\lib and Pi1541's make file the root folder.
You will need to edit the make files to set GCC_BASE to the location of your GNU tools.
(If anyone knows how to fix this requirement then please fix it. arm-none-eabi-gcc can find the include paths why can't arm-none-eabi-ld find the library paths?)
You need to build uspi\lib first.
Change to uspi\lib and make.
Change back to the root folder of the project and again make.
This will build kernel.img

45
ROMs.cpp Normal file
View File

@ -0,0 +1,45 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#include "ROMs.h"
#include "debug.h"
#include <strings.h>
void ROMs::ResetCurrentROMIndex()
{
currentROMIndex = lastManualSelectedROMIndex;
DEBUG_LOG("Reset ROM back to %d %s\r\n", currentROMIndex, ROMNames[currentROMIndex]);
}
void ROMs::SelectROM(const char* ROMName)
{
unsigned index;
for (index = 0; index < MAX_ROMS; ++index)
{
if (ROMNames[index] && strcasecmp(ROMNames[index], ROMName) == 0)
{
DEBUG_LOG("LST switching ROM %d %s\r\n", index, ROMNames[index]);
currentROMIndex = index;
break;
}
}
}

46
ROMs.h Normal file
View File

@ -0,0 +1,46 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#ifndef ROMs_H
#define ROMs_H
#include "types.h"
class ROMs
{
public:
void SelectROM(const char* ROMName);
inline u8 Read(u16 address)
{
return ROMImages[currentROMIndex][address & 0x3fff];
}
void ResetCurrentROMIndex();
static const int ROM_SIZE = 16384;
static const int MAX_ROMS = 8;
unsigned char ROMImages[MAX_ROMS][ROM_SIZE];
char ROMNames[MAX_ROMS][256];
bool ROMValid[MAX_ROMS];
unsigned currentROMIndex;
unsigned lastManualSelectedROMIndex;
};
#endif

311
Screen.cpp Normal file
View File

@ -0,0 +1,311 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#include "Screen.h"
#include "CBMFont.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "debug.h"
#include "Petscii.h"
#include "stb_image_config.h"
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
extern "C"
{
#include "rpi-mailbox-interface.h"
#include "xga_font_data.h"
}
extern u32 RPi_CpuId;
static const int BitFontHt = 16;
static const int BitFontWth = 8;
void Screen::Open(u32 widthDesired, u32 heightDesired, u32 colourDepth)
{
rpi_mailbox_property_t* mp;
//int width = 0;
//int height = 0;
//int depth = 0;
RPI_PropertyInit();
RPI_PropertyAddTag(TAG_GET_PHYSICAL_SIZE);
RPI_PropertyAddTag(TAG_GET_VIRTUAL_SIZE);
RPI_PropertyAddTag(TAG_GET_DEPTH);
RPI_PropertyProcess();
//if ((mp = RPI_PropertyGet(TAG_GET_PHYSICAL_SIZE)))
//{
// width = mp->data.buffer_32[0];
// height = mp->data.buffer_32[1];
//}
//if ((mp = RPI_PropertyGet(TAG_GET_DEPTH)))
// depth = mp->data.buffer_32[0];
//DEBUG_LOG("width = %d height = %d depth = %d\r\n", width, height, depth);
do
{
RPI_PropertyInit();
RPI_PropertyAddTag(TAG_ALLOCATE_BUFFER);
RPI_PropertyAddTag(TAG_SET_PHYSICAL_SIZE, widthDesired, heightDesired);
RPI_PropertyAddTag(TAG_SET_VIRTUAL_SIZE, widthDesired, heightDesired); // Don't need to double buffer (yet).
RPI_PropertyAddTag(TAG_SET_DEPTH, colourDepth);
RPI_PropertyAddTag(TAG_GET_PITCH);
RPI_PropertyAddTag(TAG_GET_PHYSICAL_SIZE);
RPI_PropertyAddTag(TAG_GET_DEPTH);
RPI_PropertyProcess();
if ((mp = RPI_PropertyGet(TAG_GET_PHYSICAL_SIZE)))
{
width = mp->data.buffer_32[0];
height = mp->data.buffer_32[1];
}
if ((mp = RPI_PropertyGet(TAG_GET_DEPTH)))
bpp = mp->data.buffer_32[0];
if ((mp = RPI_PropertyGet(TAG_GET_PITCH)))
pitch = mp->data.buffer_32[0];
if ((mp = RPI_PropertyGet(TAG_ALLOCATE_BUFFER)))
framebuffer = (unsigned char*)(mp->data.buffer_32[0] & 0x3FFFFFFF);
}
while (framebuffer == 0);
//RPI_PropertyInit();
//RPI_PropertyAddTag(TAG_SET_PALETTE, palette);
//RPI_PropertyProcess();
switch (bpp)
{
case 32:
plotPixelFn = &Screen::PlotPixel32;
break;
case 24:
plotPixelFn = &Screen::PlotPixel24;
break;
default:
case 16:
plotPixelFn = &Screen::PlotPixel16;
break;
case 8:
plotPixelFn = &Screen::PlotPixel8;
break;
}
opened = true;
}
void Screen::PlotPixel32(u32 pixel_offset, RGBA Colour)
{
*((volatile RGBA*)&framebuffer[pixel_offset]) = Colour;
}
void Screen::PlotPixel24(u32 pixel_offset, RGBA Colour)
{
framebuffer[pixel_offset++] = BLUE(Colour);
framebuffer[pixel_offset++] = GREEN(Colour);
framebuffer[pixel_offset++] = RED(Colour);
}
void Screen::PlotPixel16(u32 pixel_offset, RGBA Colour)
{
*(unsigned short*)&framebuffer[pixel_offset] = ((RED(Colour) >> 3) << 11) | ((GREEN(Colour) >> 2) << 5) | (BLUE(Colour) >> 3);
}
void Screen::PlotPixel8(u32 pixel_offset, RGBA Colour)
{
framebuffer[pixel_offset++] = RED(Colour);
}
void Screen::ClipRect(u32& x1, u32& y1, u32& x2, u32& y2)
{
if (x1 > width) x1 = width;
if (y1 > height) y1 = height;
if (x2 > width) x2 = width;
if (y2 > height) y2 = height;
}
void Screen::ClearArea(u32 x1, u32 y1, u32 x2, u32 y2, RGBA colour)
{
ClipRect(x1, y1, x2, y2);
for (u32 y = y1; y < y2; y++)
{
u32 line = y * pitch;
for (u32 x = x1; x < x2; x++)
{
u32 pixel_offset = (x * (bpp >> 3)) + line;
(this->*Screen::plotPixelFn)(pixel_offset, colour);
}
}
}
void Screen::ScrollArea(u32 x1, u32 y1, u32 x2, u32 y2)
{
ClipRect(x1, y1, x2, y2);
if (x2 - 1 <= x1)
return;
for (u32 y = y1; y < y2; y++)
{
u32 line = y * pitch;
for (u32 x = x1; x < (x2 - 1); x++)
{
u32 pixel_offset = ((x + 1) * (bpp >> 3)) + line;
u32 pixel_offsetDest = (x * (bpp >> 3)) + line;
*(unsigned short*)&framebuffer[pixel_offsetDest] = *(unsigned short*)&framebuffer[pixel_offset];
}
}
}
void Screen::Clear(RGBA colour)
{
ClearArea(0, 0, width, height, colour);
}
void Screen::WriteChar(bool petscii, u32 x, u32 y, unsigned char c, RGBA colour)
{
if (opened)
{
u32 fontHeight;
const unsigned char* fontBitMap;
if (petscii)
{
fontBitMap = CMBFont;
fontHeight = 8;
c = petscii2screen(c);
}
else
{
fontBitMap = avpriv_vga16_font;
fontHeight = BitFontHt;
}
for (u32 py = 0; py < fontHeight; ++py)
{
if (y + py > height)
return;
unsigned char b = fontBitMap[c * fontHeight + py];
int yoffs = (y + py) * pitch;
for (int px = 0; px < 8; ++px)
{
if (x + px > width)
continue;
int pixel_offset = ((px + x) * (bpp >> 3)) + yoffs;
if ((b & 0x80) == 0x80)
(this->*Screen::plotPixelFn)(pixel_offset, colour);
b = b << 1;
}
}
}
}
void Screen::PlotPixel(u32 x, u32 y, RGBA colour)
{
int pixel_offset = (x * (bpp >> 3)) + (y * pitch);
(this->*Screen::plotPixelFn)(pixel_offset, colour);
}
void Screen::DrawLine(u32 x1, u32 y1, u32 x2, u32 y2, RGBA colour)
{
ClipRect(x1, y1, x2, y2);
int dx0, dy0, ox, oy, eulerMax;
dx0 = (int)(x2 - x1);
dy0 = (int)(y2 - y1);
eulerMax = abs(dx0);
if (abs(dy0) > eulerMax) eulerMax = abs(dy0);
for (int i = 0; i <= eulerMax; i++)
{
ox = ((dx0 * i) / eulerMax) + x1;
oy = ((dy0 * i) / eulerMax) + y1;
int pixel_offset = (ox * (bpp >> 3)) + (oy * pitch);
(this->*Screen::plotPixelFn)(pixel_offset, colour);
}
}
void Screen::DrawLineV(u32 x, u32 y1, u32 y2, RGBA colour)
{
//ClipRect(x, y1, x, y2);
for (u32 y = y1; y <= y2; ++y)
{
int pixel_offset = (x * (bpp >> 3)) + (y * pitch);
(this->*Screen::plotPixelFn)(pixel_offset, colour);
}
}
u32 Screen::PrintText(bool petscii, u32 x, u32 y, char *ptr, RGBA TxtColour, RGBA BkColour, bool measureOnly, u32* width, u32* height)
{
int xCursor = x;
int yCursor = y;
int len = 0;
u32 fontHeight;
if (petscii) fontHeight = 8;
else fontHeight = BitFontHt;
if (width) *width = 0;
while (*ptr != 0)
{
char c = *ptr++;
if ((c != '\r') && (c != '\n'))
{
if (!measureOnly)
{
ClearArea(xCursor, yCursor, xCursor + BitFontWth, yCursor + fontHeight, BkColour);
WriteChar(petscii, xCursor, yCursor, c, TxtColour);
}
xCursor += BitFontWth;
if (width) *width = MAX(*width, (u32)MAX(0, xCursor));
}
else
{
xCursor = x;
yCursor += fontHeight;
}
len++;
}
if (height) *height = yCursor;
return len;
}
u32 Screen::MeasureText(bool petscii, char *ptr, u32* width, u32* height)
{
return PrintText(petscii, 0, 0, ptr, 0, 0, true, width, height);
}
void Screen::PlotImage(u32* image, int x, int y, int w, int h)
{
int px;
int py;
int i = 0;
for (py = 0; py < h; ++py)
{
for (px = 0; px < w; ++px)
{
PlotPixel(x + px, y + py, image[i++]);
}
}
}

88
Screen.h Normal file
View File

@ -0,0 +1,88 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#ifndef SCREEN_H
#define SCREEN_H
#include "types.h"
typedef u32 RGBA;
#define RED(colour) ( (u8)(((u32)colour) & 0xFF) )
#define GREEN(colour) ( (u8)(((u32)colour >> 8) & 0xFF) )
#define BLUE(colour) ( (u8)(((u32)colour >> 16) & 0xFF) )
#define ALPHA(colour) ( (u8)(((u32)colour >> 24) & 0xFF) )
#define RGBA(r, g, b, a) ( ((u32)((u8)(r))) | ((u32)((u8)(g)) << 8) | ((u32)((u8)(b)) << 16) | ((u32)((u8)(a)) << 24) )
class Screen
{
public:
Screen()
: opened(false)
, width(0)
, height(0)
, bpp(0)
, pitch(0)
, framebuffer(0)
{
}
void Open(u32 width, u32 height, u32 colourDepth);
void ClearArea(u32 x1, u32 y1, u32 x2, u32 y2, RGBA colour);
void Clear(RGBA colour);
void ScrollArea(u32 x1, u32 y1, u32 x2, u32 y2);
void WriteChar(bool petscii, u32 x, u32 y, unsigned char c, RGBA colour);
u32 PrintText(bool petscii, u32 xPos, u32 yPos, char *ptr, RGBA TxtColour = RGBA(0xff, 0xff, 0xff, 0xff), RGBA BkColour = RGBA(0, 0, 0, 0xFF), bool measureOnly = false, u32* width = 0, u32* height = 0);
u32 MeasureText(bool petscii, char *ptr, u32* width = 0, u32* height = 0);
void DrawLine(u32 x1, u32 y1, u32 x2, u32 y2, RGBA colour);
void DrawLineV(u32 x, u32 y1, u32 y2, RGBA colour);
void PlotPixel(u32 x, u32 y, RGBA colour);
void PlotImage(u32* image, int x, int y, int w, int h);
u32 Width() const { return width; }
u32 Height() const { return height; }
private:
typedef void (Screen::*PlotPixelFunction)(u32 pixel_offset, RGBA Colour);
void PlotPixel32(u32 pixel_offset, RGBA Colour);
void PlotPixel24(u32 pixel_offset, RGBA Colour);
void PlotPixel16(u32 pixel_offset, RGBA Colour);
void PlotPixel8(u32 pixel_offset, RGBA Colour);
void ClipRect(u32& x1, u32& y1, u32& x2, u32& y2);
PlotPixelFunction plotPixelFn;
bool opened;
u32 width;
u32 height;
u32 bpp;
u32 pitch;
volatile u8* framebuffer;
};
#endif

51
Singleton.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef _Singleton_h_
#define _Singleton_h_
#include "Types.h"
template <typename T>
class Singleton
{
public:
static T* Instance()
{
if (m_pInstance == 0)
m_pInstance = new T;
//assert(m_pInstance != 0);
return m_pInstance;
};
static void DestroyInstance()
{
delete m_pInstance;
m_pInstance = 0;
};
protected:
Singleton()
{
};
virtual ~Singleton()
{
};
static T* m_pInstance;
private:
Singleton(const Singleton& source)
{
};
};
template <typename T> T* Singleton<T>::m_pInstance = 0;
#endif

121
Timer.c Normal file
View File

@ -0,0 +1,121 @@
#include "timer.h"
#include "bcm2835int.h"
#include "rpiHardware.h"
// Support functions for uspi
// Based on env\include\uspienv\timer.h
// The number of 1MHz ticks for 10ms
#define TEN_MILLISECS (CLOCKHZ / HZ)
static unsigned Ticks = 0;
static volatile TKernelTimer m_KernelTimer[KERNEL_TIMERS]; // TODO: should be linked list
static void TimerPollKernelTimers()
{
//EnterCritical();
for (unsigned hTimer = 0; hTimer < KERNEL_TIMERS; hTimer++)
{
volatile TKernelTimer* pTimer = &m_KernelTimer[hTimer];
TKernelTimerHandler* pHandler = pTimer->m_pHandler;
if (pHandler != 0)
{
if ((int)(pTimer->m_nElapsesAt - Ticks) <= 0)
{
pTimer->m_pHandler = 0;
(*pHandler)(hTimer + 1, pTimer->m_pParam, pTimer->m_pContext);
}
}
}
//LeaveCritical();
}
static void TimerInterruptHandler(void* pParam)
{
DataMemBarrier();
//assert(read32(ARM_SYSTIMER_CS) & (1 << 3));
u32 nCompare = read32(ARM_SYSTIMER_C3) + TEN_MILLISECS;
write32(ARM_SYSTIMER_C3, nCompare);
if (nCompare < read32(ARM_SYSTIMER_CLO)) // time may drift
{
nCompare = read32(ARM_SYSTIMER_CLO) + TEN_MILLISECS;
write32(ARM_SYSTIMER_C3, nCompare);
}
write32(ARM_SYSTIMER_CS, 1 << 3);
DataMemBarrier();
++Ticks;
TimerPollKernelTimers();
}
void TimerSystemInitialize()
{
InterruptSystemConnectIRQ(ARM_IRQ_TIMER3, TimerInterruptHandler, 0);
DataMemBarrier();
write32(ARM_SYSTIMER_CLO, -(30 * CLOCKHZ)); // timer wraps soon, to check for problems
// Interrupt every 10ms
write32(ARM_SYSTIMER_C3, read32(ARM_SYSTIMER_CLO) + TEN_MILLISECS);
DataMemBarrier();
for (unsigned hTimer = 0; hTimer < KERNEL_TIMERS; hTimer++)
{
m_KernelTimer[hTimer].m_pHandler = 0;
}
}
unsigned TimerStartKernelTimer(unsigned nDelay, TKernelTimerHandler* pHandler, void* pParam, void* pContext)
{
//EnterCritical();
// DEBUG_LOG("Timer started\r\n");
unsigned hTimer;
for (hTimer = 0; hTimer < KERNEL_TIMERS; hTimer++)
{
if (m_KernelTimer[hTimer].m_pHandler == 0)
{
break;
}
}
if (hTimer >= KERNEL_TIMERS)
{
//LeaveCritical();
DEBUG_LOG("System limit of kernel timers exceeded\r\n");
return 0;
}
//assert(pHandler != 0);
m_KernelTimer[hTimer].m_pHandler = pHandler;
m_KernelTimer[hTimer].m_nElapsesAt = Ticks+nDelay;
m_KernelTimer[hTimer].m_pParam = pParam;
m_KernelTimer[hTimer].m_pContext = pContext;
//LeaveCritical();
return hTimer+1;
}
void TimerCancelKernelTimer(unsigned hTimer)
{
//assert(1 <= hTimer && hTimer <= KERNEL_TIMERS);
m_KernelTimer[hTimer-1].m_pHandler = 0;
}

46
Timer.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef Timer_H
#define Timer_H
#include <stddef.h>
#include "interrupt.h"
// Support functions for uspi
// Based on env\include\uspienv\timer.h
#ifdef __cplusplus
extern "C" {
#endif
#define KERNEL_TIMERS 20
#define HZ 100 // ticks per second
#define MSEC2HZ(msec) ((msec) * HZ / 1000)
typedef void TKernelTimerHandler(unsigned hTimer, void* pParam, void* pContext);
typedef struct TKernelTimer
{
TKernelTimerHandler* m_pHandler;
unsigned m_nElapsesAt;
void* m_pParam;
void* m_pContext;
}
TKernelTimer;
void TimerSystemInitialize();
#define CLOCKHZ 1000000
unsigned TimerStartKernelTimer(
unsigned nDelay, // in HZ units
TKernelTimerHandler* pHandler,
void* pParam,
void* pContext);
void TimerCancelKernelTimer(unsigned hTimer);
#ifdef __cplusplus
}
#endif
#endif

42
armc-cppstubs.cpp Normal file
View File

@ -0,0 +1,42 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#include <stdlib.h>
extern "C" void *__dso_handle = 0;
void* operator new(size_t size) noexcept
{
return malloc(size);
}
void operator delete (void* ptr)
{
free(ptr);
}
namespace std
{
void __throw_bad_alloc()
{
}
void __throw_length_error(char const*e)
{
}
}

69
armc-cstartup.c Normal file
View File

@ -0,0 +1,69 @@
/*
Part of the Raspberry-Pi Bare Metal Tutorials
Copyright (c) 2013, Brian Sidebotham
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
extern int __bss_start__;
extern int __bss_end__;
extern void kernel_main( unsigned int r0, unsigned int r1, unsigned int atags );
extern void __libc_init_array();
void _init(void)
{
}
void _cstartup( unsigned int r0, unsigned int r1, unsigned int r2 )
{
int* bss = &__bss_start__;
int* bss_end = &__bss_end__;
/*
Clear the BSS section
See http://en.wikipedia.org/wiki/.bss for further information on the
BSS section
See https://sourceware.org/newlib/libc.html#Stubs for further
information on the c-library stubs
*/
while( bss < bss_end )
*bss++ = 0;
__libc_init_array();
/* We should never return from main ... */
kernel_main( r0, r1, r2 );
/* ... but if we do, safely trap here */
while(1)
{
/* EMPTY! */
}
}

246
armc-cstubs.c Normal file
View File

@ -0,0 +1,246 @@
/*
Part of the Raspberry-Pi Bare Metal Tutorials
Copyright (c) 2013-2015, Brian Sidebotham
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/* For more information about this file, please visit:
https://sourceware.org/newlib/libc.html#Stubs
These are the newlib C-Library stubs for the valvers Raspberry-Pi bare-metal
tutorial */
/*
Graceful failure is permitted by returning an error code. A minor
complication arises here: the C library must be compatible with development
environments that supply fully functional versions of these subroutines.
Such environments usually return error codes in a global errno. However,
the Red Hat newlib C library provides a macro definition for errno in the
header file errno.h, as part of its support for reentrant routines (see
Reentrancy).
The bridge between these two interpretations of errno is straightforward:
the C library routines with OS interface calls capture the errno values
returned globally, and record them in the appropriate field of the
reentrancy structure (so that you can query them using the errno macro from
errno.h).
This mechanism becomes visible when you write stub routines for OS
interfaces. You must include errno.h, then disable the macro, like this:
*/
#include <errno.h>
#undef errno
extern int errno;
/* Required include for fstat() */
#include <sys/stat.h>
/* Required include for times() */
#include <sys/times.h>
/* Prototype for the UART write function */
#include "rpi-aux.h"
/* A pointer to a list of environment variables and their values. For a minimal
environment, this empty list is adequate: */
char *__env[1] =
{ 0 };
char **environ = __env;
/* A helper function written in assembler to aid us in allocating memory */
//extern caddr_t _get_stack_pointer(void);
/* Never return from _exit as there's no OS to exit to, so instead we trap
here */
void _exit(int status)
{
/* Stop the compiler complaining about unused variables by "using" it */
(void) status;
while (1)
{
/* TRAP HERE */
}
}
/* There's currently no implementation of a file system because there's no
file system! */
int _close(int file)
{
return -1;
}
/* Transfer control to a new process. Minimal implementation (for a system
without processes): */
int execve(char *name, char **argv, char **env)
{
errno = ENOMEM;
return -1;
}
/* Create a new process. Minimal implementation (for a system without
processes): */
int fork(void)
{
errno = EAGAIN;
return -1;
}
/* Status of an open file. For consistency with other minimal implementations
in these examples, all files are regarded as character special devices. The
sys/stat.h header file required is distributed in the include subdirectory
for this C library. */
int _fstat(int file, struct stat *st)
{
st->st_mode = S_IFCHR;
return 0;
}
/* Process-ID; this is sometimes used to generate strings unlikely to conflict
with other processes. Minimal implementation, for a system without
processes: */
int getpid(void)
{
return 1;
}
int _getpid(void)
{
return 1;
}
/* Query whether output stream is a terminal. For consistency with the other
minimal implementations, which only support output to stdout, this minimal
implementation is suggested: */
int _isatty(int file)
{
return 1;
}
/* Send a signal. Minimal implementation: */
int kill(int pid, int sig)
{
errno = EINVAL;
return -1;
}
int _kill(int pid, int sig)
{
errno = EINVAL;
return -1;
}
/* Establish a new name for an existing file. Minimal implementation: */
int link( char *old, char *pnew )
{
errno = EMLINK;
return -1;
}
/* Set position in a file. Minimal implementation: */
int _lseek(int file, int ptr, int dir)
{
return 0;
}
/* Open a file. Minimal implementation: */
int open(const char *name, int flags, int mode)
{
return -1;
}
/* Read from a file. Minimal implementation: */
int _read(int file, char *ptr, int len)
{
return 0;
}
/* Increase program data space. As malloc and related functions depend on this,
it is useful to have a working implementation. The following suffices for a
standalone system; it exploits the symbol _end automatically defined by the
GNU linker. */
caddr_t _sbrk(int incr)
{
extern char _end;
static char* heap_end = 0;
char* prev_heap_end;
if (heap_end == 0)
heap_end = &_end;
prev_heap_end = heap_end;
heap_end += incr;
return (caddr_t) prev_heap_end;
}
/* Status of a file (by name). Minimal implementation: */
int stat(const char *file, struct stat *st)
{
st->st_mode = S_IFCHR;
return 0;
}
/* Timing information for current process. Minimal implementation: */
clock_t times(struct tms *buf)
{
return -1;
}
/* Remove a file's directory entry. Minimal implementation: */
int unlink(char *name)
{
errno = ENOENT;
return -1;
}
/* Wait for a child process. Minimal implementation: */
int wait(int *status)
{
errno = ECHILD;
return -1;
}
void outbyte(char b)
{
RPI_AuxMiniUartWrite(b);
}
/* Write to a file. libc subroutines will use this system routine for output to
all files, including stdoutso if you need to generate any output, for
example to a serial port for debugging, you should make your minimal write
capable of doing this. The following minimal implementation is an
incomplete example; it relies on a outbyte subroutine (not shown; typically,
you must write this in assembler from examples provided by your hardware
manufacturer) to actually perform the output. */
int _write(int file, char *ptr, int len)
{
int todo;
for (todo = 0; todo < len; todo++)
outbyte(*ptr++);
return len;
}

510
armc-start.S Normal file
View File

@ -0,0 +1,510 @@
// Part of the Raspberry-Pi Bare Metal Tutorials
// Copyright (c) 2013-2015, Brian Sidebotham
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// Relocate to just below 32MB
#include "defs.h"
.equ STACK_SIZE, 0x00100000
.equ C0_SVR_STACK, 0
.equ C0_IRQ_STACK, (STACK_SIZE*1)
.equ C0_FIQ_STACK, STACK_SIZE*2
.equ C0_USER_STACK, STACK_SIZE*4
.equ C0_ABORT_STACK, STACK_SIZE*5
.equ C0_UNDEFINED_STACK, STACK_SIZE*6
#if defined(RPI2) || defined(RPI3)
.equ C1_SVR_STACK, STACK_SIZE*7
.equ C1_IRQ_STACK, STACK_SIZE*8
.equ C1_FIQ_STACK, STACK_SIZE*9
.equ C1_USER_STACK, STACK_SIZE*10
.equ C1_ABORT_STACK, STACK_SIZE*11
.equ C1_UNDEFINED_STACK, STACK_SIZE*12
#endif
.equ SCTLR_ENABLE_DATA_CACHE, 0x4
.equ SCTLR_ENABLE_BRANCH_PREDICTION, 0x800
.equ SCTLR_ENABLE_INSTRUCTION_CACHE, 0x1000
.section ".text.startup"
.global _start
.global _get_cpsr
.global _get_stack_pointer
.global _exception_table
.global _enable_interrupts
.global _disable_interrupts
.global _enable_unaligned_access
.global _enable_l1_cache
.global _invalidate_icache
.global _invalidate_dcache
.global _clean_invalidate_dcache
.global _invalidate_dcache_mva
.global _clean_invalidate_dcache_mva
.global _invalidate_dtlb
.global _invalidate_dtlb_mva
.global _data_memory_barrier
#ifdef HAS_MULTICORE
.global _get_core
.global _init_core
.global _spin_core
#endif
#if defined(HAS_40PINS)
.global _toggle_test_pin
#endif
// From the ARM ARM (Architecture Reference Manual). Make sure you get the
// ARMv5 documentation which includes the ARMv6 documentation which is the
// correct processor type for the Broadcom BCM2835. The ARMv6-M manuals
// available on the ARM website are for Cortex-M parts only and are very
// different.
//
// See ARM section A2.2 (Processor Modes)
.equ CPSR_MODE_USER, 0x10
.equ CPSR_MODE_FIQ, 0x11
.equ CPSR_MODE_IRQ, 0x12
.equ CPSR_MODE_SVR, 0x13
.equ CPSR_MODE_ABORT, 0x17
.equ CPSR_MODE_HYP, 0x1A
.equ CPSR_MODE_UNDEFINED, 0x1B
.equ CPSR_MODE_SYSTEM, 0x1F
.equ CPSR_MODE_MASK, 0x1F
// See ARM section A2.5 (Program status registers)
.equ CPSR_A_BIT, 0x100
.equ CPSR_IRQ_INHIBIT, 0x80
.equ CPSR_FIQ_INHIBIT, 0x40
.equ CPSR_THUMB, 0x20
_start:
ldr pc, _reset_h
ldr pc, _undefined_instruction_vector_h
ldr pc, _software_interrupt_vector_h
ldr pc, _prefetch_abort_vector_h
ldr pc, _data_abort_vector_h
ldr pc, _unused_handler_h
ldr pc, _interrupt_vector_h
ldr pc, _fast_interrupt_vector_h
_reset_h: .word _reset_
_undefined_instruction_vector_h: .word _undefined_instruction_handler_
_software_interrupt_vector_h: .word _swi_handler_
_prefetch_abort_vector_h: .word _prefetch_abort_handler_
_data_abort_vector_h: .word _data_abort_handler_
_unused_handler_h: .word _reset_
_interrupt_vector_h: .word arm_irq_handler
_fast_interrupt_vector_h: .word arm_fiq_handler
.section ".text._reset_"
_reset_:
BL _enable_l1_cache
#ifdef HAS_MULTICORE
#ifdef KERNEL_OLD
// if kernel_old=1 all cores are running and we need to sleep 1-3
// if kernel_old=0 then just core0 is running, and core 1-3 are waiting
// on a mailbox write to be woken up.
//
// Test which core we are running on
mrc p15, 0, r0, c0, c0, 5
ands r0, #3
beq _core_continue
// Put cores 1-3 into a tight loop
_core_loop:
wfi
b _core_loop
_core_continue:
#else
// if kernel_old=0 enter in HYP mode and need to force a switch to SVC mode
//
// for now we assume kernel_old=1 and don't execute this core
//
// The logs show:
// SVC mode: cpsr ends with 1d3
// HYP mode: cpsr ends with 1a3
mrs r0, cpsr
eor r0, r0, #CPSR_MODE_HYP
tst r0, #CPSR_MODE_MASK
bic r0 , r0 , #CPSR_MODE_MASK
orr r0 , r0 , #CPSR_IRQ_INHIBIT | CPSR_FIQ_INHIBIT | CPSR_MODE_SVR
bne _not_in_hyp_mode
orr r0, r0, #CPSR_A_BIT
adr lr, _reset_continue
msr spsr_cxsf, r0
.word 0xE12EF30E // msr_elr_hyp lr
.word 0xE160006E // eret
_not_in_hyp_mode:
msr cpsr_c, r0
_reset_continue:
#endif
#endif
// We enter execution in supervisor mode. For more information on
// processor modes see ARM Section A2.2 (Processor Modes)
ldr r0,=_start
mov r1, #0x00000000
ldmia r0!,{r2, r3, r4, r5, r6, r7, r8, r9}
stmia r1!,{r2, r3, r4, r5, r6, r7, r8, r9}
ldmia r0!,{r2, r3, r4, r5, r6, r7, r8, r9}
stmia r1!,{r2, r3, r4, r5, r6, r7, r8, r9}
// Initialise Stack Pointers ---------------------------------------------
ldr r4,=_start
// We're going to use interrupt mode, so setup the interrupt mode
// stack pointer which differs to the application stack pointer:
mov r0, #(CPSR_MODE_IRQ | CPSR_IRQ_INHIBIT | CPSR_FIQ_INHIBIT )
msr cpsr_c, r0
sub sp, r4, #C0_IRQ_STACK
// Also setup the stack used for FIQs
mov r0, #(CPSR_MODE_FIQ | CPSR_IRQ_INHIBIT | CPSR_FIQ_INHIBIT )
msr cpsr_c, r0
sub sp, r4, #C0_FIQ_STACK
// Also setup the stack used for undefined exceptions
mov r0, #(CPSR_MODE_UNDEFINED | CPSR_IRQ_INHIBIT | CPSR_FIQ_INHIBIT )
msr cpsr_c, r0
sub sp, r4, #C0_UNDEFINED_STACK
// Also setup the stack used for prefetch and data abort exceptions
mov r0, #(CPSR_MODE_ABORT | CPSR_IRQ_INHIBIT | CPSR_FIQ_INHIBIT )
msr cpsr_c, r0
sub sp, r4, #C0_ABORT_STACK
// Finally, a user/system mode stack, although the application will likely reset this
mov r0, #(CPSR_MODE_SYSTEM | CPSR_IRQ_INHIBIT | CPSR_FIQ_INHIBIT )
msr cpsr_c, r0
sub sp, r4, #C0_USER_STACK
// Switch back to supervisor mode (our application mode) and
// set the stack pointer. Remember that the stack works its way
// down memory, our heap will work it's way up from after the
// application.
mov r0, #(CPSR_MODE_SVR | CPSR_IRQ_INHIBIT | CPSR_FIQ_INHIBIT )
msr cpsr_c, r0
sub sp, r4, #C0_SVR_STACK
// Enable VFP ------------------------------------------------------------
#ifdef HAS_MULTICORE
//1. Set the CPACR for access to CP10 and CP11, and clear the ASEDIS and D32DIS bits:
ldr r0, =(0xf << 20)
mcr p15, 0, r0, c1, c0, 2
// 2. Set the FPEXC EN bit to enable the NEON MPE:
mov r0, #0x40000000
vmsr fpexc, r0
#else
// r1 = Access Control Register
MRC p15, #0, r1, c1, c0, #2
// enable full access for p10,11
ORR r1, r1, #(0xf << 20)
// ccess Control Register = r1
MCR p15, #0, r1, c1, c0, #2
MOV r1, #0
// flush prefetch buffer because of FMXR below
MCR p15, #0, r1, c7, c5, #4
// and CP 10 & 11 were only just enabled
// Enable VFP itself
MOV r0,#0x40000000
// FPEXC = r0
FMXR FPEXC, r0
#endif
// The c-startup function which we never return from. This function will
// initialise the ro data section (most things that have the const
// declaration) and initialise the bss section variables to 0 (generally
// known as automatics). It'll then call main
b _cstartup
arm_fiq_handler:
arm_irq_handler:
//subs pc, lr, #4
sub lr, lr, #4 /* lr: return address */
stmfd sp!, {r0-r12, lr} /* save r0-r12 and return address */
bl InterruptHandler
ldmfd sp!, {r0-r12, pc}^ /* restore registers and return */
.section ".text._get_stack_pointer"
_get_stack_pointer:
mov r0, sp
mov pc, lr
.section ".text._get_cpsr"
_get_cpsr:
mrs r0, cpsr
mov pc, lr
.section ".text._enable_interrupts"
_enable_interrupts:
mrs r0, cpsr
bic r0, r0, #CPSR_IRQ_INHIBIT | CPSR_FIQ_INHIBIT
msr cpsr_c, r0
mov pc, lr
.section ".text._disable_interrupts"
_disable_interrupts:
mrs r0, cpsr
orr r1, r0, #CPSR_IRQ_INHIBIT | CPSR_FIQ_INHIBIT
msr cpsr_c, r1
mov pc, lr
.section ".text._undefined_instruction_handler_"
_undefined_instruction_handler_:
stmfd sp!, {r0-r12, lr}
mrs r0, spsr // Get spsr.
stmfd sp!, {r0} // Store spsr onto stack.
mov r0, sp
bl undefined_instruction_handler
.section ".text._prefetch_abort_handler_"
_prefetch_abort_handler_:
stmfd sp!, {r0-r12, lr}
mrs r0, spsr // Get spsr.
stmfd sp!, {r0} // Store spsr onto stack.
mov r0, sp
bl prefetch_abort_handler
.section ".text._data_abort_handler_"
_data_abort_handler_:
stmfd sp!, {r0-r12, lr}
mrs r0, spsr // Get spsr.
stmfd sp!, {r0} // Store spsr onto stack.
mov r0, sp
bl data_abort_handler
.section ".text._swi_handler_"
_swi_handler_:
stmfd sp!, {r0-r12, lr}
mrs r0, spsr // Get spsr.
stmfd sp!, {r0} // Store spsr onto stack.
mov r0, sp
bl swi_handler
.section ".text._enable_unaligned_access"
_enable_unaligned_access:
mrc p15, 0, r0, c1, c0, 0 // read SCTLR
bic r0, r0, #2 // A (no unaligned access fault)
orr r0, r0, #1 << 22 // U (v6 unaligned access model)
mcr p15, 0, r0, c1, c0, 0 // write SCTLR
mov pc, lr
// Enable L1 Cache -------------------------------------------------------
.section ".text._enable_l1_cache"
_enable_l1_cache:
// R0 = System Control Register
mrc p15,0,r0,c1,c0,0
// Enable caches and branch prediction
orr r0,#SCTLR_ENABLE_BRANCH_PREDICTION
orr r0,#SCTLR_ENABLE_DATA_CACHE
orr r0,#SCTLR_ENABLE_INSTRUCTION_CACHE
// System Control Register = R0
mcr p15,0,r0,c1,c0,0
mov pc, lr
.section ".text._invalidate_icache"
_invalidate_icache:
mov r0, #0
mcr p15, 0, r0, c7, c5, 0
mov pc, lr
.section ".text._invalidate_dcache"
_invalidate_dcache:
mov r0, #0
mcr p15, 0, r0, c7, c6, 0
mov pc, lr
.section ".text._clean_invalidate_dcache"
_clean_invalidate_dcache:
mov r0, #0
mcr p15, 0, r0, c7, c14, 0
mov pc, lr
.section ".text._invalidate_dcache_mva"
_invalidate_dcache_mva:
mcr p15, 0, r0, c7, c6, 1
mov pc, lr
.section ".text._clean_invalidate_dcache_mva"
_clean_invalidate_dcache_mva:
mcr p15, 0, r0, c7, c14, 1
mov pc, lr
.section ".text._invalidate_dtlb"
_invalidate_dtlb:
mov r0, #0
mcr p15, 0, r0, c8, c6, 0
mov pc, lr
.section ".text._invalidate_dtlb_mva"
_invalidate_dtlb_mva:
mcr p15, 0, r0, c8, c6, 1
mov pc, lr
.section ".text._data_memory_barrier"
_data_memory_barrier:
#if defined(RPI2) || defined(RPI3)
dmb
#else
mov r0, #0
mcr p15, 0, r0, c7, c10, 5
#endif
mov pc, lr
#ifdef USE_MULTICORE
_init_core:
// On a Raspberry Pi 2 we enter in HYP mode, and need to force a switch to supervisor mode
mrs r0, cpsr
eor r0, r0, #CPSR_MODE_HYP
tst r0, #CPSR_MODE_MASK
bic r0 , r0 , #CPSR_MODE_MASK
orr r0 , r0 , #CPSR_IRQ_INHIBIT | CPSR_FIQ_INHIBIT | CPSR_MODE_SVR
bne _init_not_in_hyp_mode
orr r0, r0, #CPSR_A_BIT
adr lr, _init_continue
msr spsr_cxsf, r0
.word 0xE12EF30E // msr_elr_hyp lr
.word 0xE160006E // eret
_init_not_in_hyp_mode:
msr cpsr_c, r0
_init_continue:
ldr r4,=_start
// Initialise Stack Pointers ---------------------------------------------
// We're going to use interrupt mode, so setup the interrupt mode
// stack pointer which differs to the application stack pointer:
mov r0, #(CPSR_MODE_IRQ | CPSR_IRQ_INHIBIT | CPSR_FIQ_INHIBIT )
msr cpsr_c, r0
sub sp, r4, # C1_IRQ_STACK
// Also setup the stack used for FIQs
mov r0, #(CPSR_MODE_FIQ | CPSR_IRQ_INHIBIT | CPSR_FIQ_INHIBIT )
msr cpsr_c, r0
sub sp, r4, # C1_FIQ_STACK
// Also setup the stack used for undefined exceptions
mov r0, #(CPSR_MODE_UNDEFINED | CPSR_IRQ_INHIBIT | CPSR_FIQ_INHIBIT )
msr cpsr_c, r0
sub sp, r4, # C1_UNDEFINED_STACK
// Also setup the stack used for prefetch and data abort exceptions
mov r0, #(CPSR_MODE_ABORT | CPSR_IRQ_INHIBIT | CPSR_FIQ_INHIBIT )
msr cpsr_c, r0
sub sp, r4, # C1_ABORT_STACK
// Finally, a user/system mode stack, although the application will likely reset this
mov r0, #(CPSR_MODE_SYSTEM | CPSR_IRQ_INHIBIT | CPSR_FIQ_INHIBIT )
msr cpsr_c, r0
sub sp, r4, # C1_USER_STACK
// Switch back to supervisor mode (our application mode) and
// set the stack pointer. Remember that the stack works its way
// down memory, our heap will work it's way up from after the
// application.
mov r0, #(CPSR_MODE_SVR | CPSR_IRQ_INHIBIT | CPSR_FIQ_INHIBIT )
msr cpsr_c, r0
sub sp, r4, # C1_SVR_STACK
// Enable VFP ------------------------------------------------------------
//1. Set the CPACR for access to CP10 and CP11, and clear the ASEDIS and D32DIS bits:
ldr r0, =(0xf << 20)
mcr p15, 0, r0, c1, c0, 2
// 2. Set the FPEXC EN bit to enable the NEON MPE:
mov r0, #0x40000000
vmsr fpexc, r0
bl run_core
#endif
#ifdef HAS_MULTICORE
// If main does return for some reason, just catch it and stay here.
_spin_core:
#ifdef DEBUG
mov r0, #'S'
bl RPI_AuxMiniUartWrite
mov r0, #'P'
bl RPI_AuxMiniUartWrite
mov r0, #'I'
bl RPI_AuxMiniUartWrite
mov r0, #'N'
bl RPI_AuxMiniUartWrite
bl _get_core
add r0, r0, #'0'
bl RPI_AuxMiniUartWrite
mov r0, #'\r'
bl RPI_AuxMiniUartWrite
mov r0, #'\n'
bl RPI_AuxMiniUartWrite
#endif
_spin_core1:
wfi
b _spin_core1
_get_core:
mrc p15, 0, r0, c0, c0, 5
and r0, #3
mov pc, lr
#endif
#ifdef HAS_40PINS
.section ".text._toggle_test_pin"
_toggle_test_pin:
mov r1, #TEST_MASK
_toggle_test_pin_loop:
ldr r2, =GPSET0
str r1, [r2]
ldr r2, =GPCLR0
str r1, [r2]
subs r0, r0, #1
bne _toggle_test_pin_loop
mov pc, lr
#endif

109
bcm2835int.h Normal file
View File

@ -0,0 +1,109 @@
//
// bcm2835int.h
//
// The IRQ list is taken from Linux and is:
// Copyright (C) 2010 Broadcom
// Copyright (C) 2003 ARM Limited
// Copyright (C) 2000 Deep Blue Solutions Ltd.
// Licensed under GPL2
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _uspienv_bcm2835int_h
#define _uspienv_bcm2835int_h
#define ARM_IRQS_PER_REG 32
#define ARM_IRQ1_BASE 0
#define ARM_IRQ2_BASE (ARM_IRQ1_BASE + ARM_IRQS_PER_REG) // Bitmask of pending shared IRQs 0-31
#define ARM_IRQBASIC_BASE (ARM_IRQ2_BASE + ARM_IRQS_PER_REG) // Bitmask of pending shared IRQs 32-63
#define ARM_IRQ_TIMER0 (ARM_IRQ1_BASE + 0)
#define ARM_IRQ_TIMER1 (ARM_IRQ1_BASE + 1)
#define ARM_IRQ_TIMER2 (ARM_IRQ1_BASE + 2)
#define ARM_IRQ_TIMER3 (ARM_IRQ1_BASE + 3)
#define ARM_IRQ_CODEC0 (ARM_IRQ1_BASE + 4)
#define ARM_IRQ_CODEC1 (ARM_IRQ1_BASE + 5)
#define ARM_IRQ_CODEC2 (ARM_IRQ1_BASE + 6)
#define ARM_IRQ_JPEG (ARM_IRQ1_BASE + 7)
#define ARM_IRQ_ISP (ARM_IRQ1_BASE + 8)
#define ARM_IRQ_USB (ARM_IRQ1_BASE + 9)
#define ARM_IRQ_3D (ARM_IRQ1_BASE + 10)
#define ARM_IRQ_TRANSPOSER (ARM_IRQ1_BASE + 11)
#define ARM_IRQ_MULTICORESYNC0 (ARM_IRQ1_BASE + 12)
#define ARM_IRQ_MULTICORESYNC1 (ARM_IRQ1_BASE + 13)
#define ARM_IRQ_MULTICORESYNC2 (ARM_IRQ1_BASE + 14)
#define ARM_IRQ_MULTICORESYNC3 (ARM_IRQ1_BASE + 15)
#define ARM_IRQ_DMA0 (ARM_IRQ1_BASE + 16)
#define ARM_IRQ_DMA1 (ARM_IRQ1_BASE + 17)
#define ARM_IRQ_DMA2 (ARM_IRQ1_BASE + 18)
#define ARM_IRQ_DMA3 (ARM_IRQ1_BASE + 19)
#define ARM_IRQ_DMA4 (ARM_IRQ1_BASE + 20)
#define ARM_IRQ_DMA5 (ARM_IRQ1_BASE + 21)
#define ARM_IRQ_DMA6 (ARM_IRQ1_BASE + 22)
#define ARM_IRQ_DMA7 (ARM_IRQ1_BASE + 23)
#define ARM_IRQ_DMA8 (ARM_IRQ1_BASE + 24)
#define ARM_IRQ_DMA9 (ARM_IRQ1_BASE + 25)
#define ARM_IRQ_DMA10 (ARM_IRQ1_BASE + 26)
#define ARM_IRQ_DMA11 (ARM_IRQ1_BASE + 27)
#define ARM_IRQ_DMA12 (ARM_IRQ1_BASE + 28)
#define ARM_IRQ_AUX (ARM_IRQ1_BASE + 29)
#define ARM_IRQ_ARM (ARM_IRQ1_BASE + 30)
#define ARM_IRQ_VPUDMA (ARM_IRQ1_BASE + 31)
#define ARM_IRQ_HOSTPORT (ARM_IRQ2_BASE + 0)
#define ARM_IRQ_VIDEOSCALER (ARM_IRQ2_BASE + 1)
#define ARM_IRQ_CCP2TX (ARM_IRQ2_BASE + 2)
#define ARM_IRQ_SDC (ARM_IRQ2_BASE + 3)
#define ARM_IRQ_DSI0 (ARM_IRQ2_BASE + 4)
#define ARM_IRQ_AVE (ARM_IRQ2_BASE + 5)
#define ARM_IRQ_CAM0 (ARM_IRQ2_BASE + 6)
#define ARM_IRQ_CAM1 (ARM_IRQ2_BASE + 7)
#define ARM_IRQ_HDMI0 (ARM_IRQ2_BASE + 8)
#define ARM_IRQ_HDMI1 (ARM_IRQ2_BASE + 9)
#define ARM_IRQ_PIXELVALVE1 (ARM_IRQ2_BASE + 10)
#define ARM_IRQ_I2CSPISLV (ARM_IRQ2_BASE + 11)
#define ARM_IRQ_DSI1 (ARM_IRQ2_BASE + 12)
#define ARM_IRQ_PWA0 (ARM_IRQ2_BASE + 13)
#define ARM_IRQ_PWA1 (ARM_IRQ2_BASE + 14)
#define ARM_IRQ_CPR (ARM_IRQ2_BASE + 15)
#define ARM_IRQ_SMI (ARM_IRQ2_BASE + 16)
#define ARM_IRQ_GPIO0 (ARM_IRQ2_BASE + 17)
#define ARM_IRQ_GPIO1 (ARM_IRQ2_BASE + 18)
#define ARM_IRQ_GPIO2 (ARM_IRQ2_BASE + 19)
#define ARM_IRQ_GPIO3 (ARM_IRQ2_BASE + 20)
#define ARM_IRQ_I2C (ARM_IRQ2_BASE + 21)
#define ARM_IRQ_SPI (ARM_IRQ2_BASE + 22)
#define ARM_IRQ_I2SPCM (ARM_IRQ2_BASE + 23)
#define ARM_IRQ_SDIO (ARM_IRQ2_BASE + 24)
#define ARM_IRQ_UART (ARM_IRQ2_BASE + 25)
#define ARM_IRQ_SLIMBUS (ARM_IRQ2_BASE + 26)
#define ARM_IRQ_VEC (ARM_IRQ2_BASE + 27)
#define ARM_IRQ_CPG (ARM_IRQ2_BASE + 28)
#define ARM_IRQ_RNG (ARM_IRQ2_BASE + 29)
#define ARM_IRQ_ARASANSDIO (ARM_IRQ2_BASE + 30)
#define ARM_IRQ_AVSPMON (ARM_IRQ2_BASE + 31)
#define ARM_IRQ_ARM_TIMER (ARM_IRQBASIC_BASE + 0)
#define ARM_IRQ_ARM_MAILBOX (ARM_IRQBASIC_BASE + 1)
#define ARM_IRQ_ARM_DOORBELL_0 (ARM_IRQBASIC_BASE + 2)
#define ARM_IRQ_ARM_DOORBELL_1 (ARM_IRQBASIC_BASE + 3)
#define ARM_IRQ_VPU0_HALTED (ARM_IRQBASIC_BASE + 4)
#define ARM_IRQ_VPU1_HALTED (ARM_IRQBASIC_BASE + 5)
#define ARM_IRQ_ILLEGAL_TYPE0 (ARM_IRQBASIC_BASE + 6)
#define ARM_IRQ_ILLEGAL_TYPE1 (ARM_IRQBASIC_BASE + 7)
#define IRQ_LINES (ARM_IRQS_PER_REG * 2 + 8)
#endif

302
cache.c Normal file
View File

@ -0,0 +1,302 @@
// Part of PiTubeDirect
// https://github.com/hoglet67/PiTubeDirect
#include <stdio.h>
#include <string.h>
#include "startup.h"
#include "rpi-base.h"
#include "cache.h"
#include "defs.h"
// Historical Note:
// Were seeing core 3 crashes if inner *and* outer both set to some flavour of WB (i.e. 1 or 3)
// The point of crashing is when the data cache is enabled
// At that point, the stack appears to vanish and the data read back is 0x55555555
// Reason turned out to be failure to correctly invalidate the entire data cache
const static unsigned l1_cached_threshold = L2_CACHED_MEM_BASE >> 20;
const static unsigned l2_cached_threshold = UNCACHED_MEM_BASE >> 20;
const static unsigned uncached_threshold = PERIPHERAL_BASE >> 20;
volatile __attribute__ ((aligned (0x4000))) unsigned PageTable[4096];
volatile __attribute__ ((aligned (0x4000))) unsigned PageTable2[NUM_4K_PAGES];
const static int aa = 1;
const static int bb = 1;
const static int shareable = 1;
#if defined(RPI2) || defined (RPI3)
#define SETWAY_LEVEL_SHIFT 1
// 4 ways x 128 sets x 64 bytes per line 32KB
#define L1_DATA_CACHE_SETS 128
#define L1_DATA_CACHE_WAYS 4
#define L1_SETWAY_WAY_SHIFT 30 // 32-Log2(L1_DATA_CACHE_WAYS)
#define L1_SETWAY_SET_SHIFT 6 // Log2(L1_DATA_CACHE_LINE_LENGTH)
#if defined(RPI2)
// 8 ways x 1024 sets x 64 bytes per line = 512KB
#define L2_CACHE_SETS 1024
#define L2_CACHE_WAYS 8
#define L2_SETWAY_WAY_SHIFT 29 // 32-Log2(L2_CACHE_WAYS)
#else
// 16 ways x 512 sets x 64 bytes per line = 512KB
#define L2_CACHE_SETS 512
#define L2_CACHE_WAYS 16
#define L2_SETWAY_WAY_SHIFT 28 // 32-Log2(L2_CACHE_WAYS)
#endif
#define L2_SETWAY_SET_SHIFT 6 // Log2(L2_CACHE_LINE_LENGTH)
// The origin of this function is:
// https://github.com/rsta2/uspi/blob/master/env/lib/synchronize.c
void InvalidateDataCache (void)
{
unsigned nSet;
unsigned nWay;
uint32_t nSetWayLevel;
// invalidate L1 data cache
for (nSet = 0; nSet < L1_DATA_CACHE_SETS; nSet++) {
for (nWay = 0; nWay < L1_DATA_CACHE_WAYS; nWay++) {
nSetWayLevel = nWay << L1_SETWAY_WAY_SHIFT
| nSet << L1_SETWAY_SET_SHIFT
| 0 << SETWAY_LEVEL_SHIFT;
asm volatile ("mcr p15, 0, %0, c7, c6, 2" : : "r" (nSetWayLevel) : "memory"); // DCISW
}
}
// invalidate L2 unified cache
for (nSet = 0; nSet < L2_CACHE_SETS; nSet++) {
for (nWay = 0; nWay < L2_CACHE_WAYS; nWay++) {
nSetWayLevel = nWay << L2_SETWAY_WAY_SHIFT
| nSet << L2_SETWAY_SET_SHIFT
| 1 << SETWAY_LEVEL_SHIFT;
asm volatile ("mcr p15, 0, %0, c7, c6, 2" : : "r" (nSetWayLevel) : "memory"); // DCISW
}
}
}
void CleanDataCache (void)
{
unsigned nSet;
unsigned nWay;
uint32_t nSetWayLevel;
// clean L1 data cache
for (nSet = 0; nSet < L1_DATA_CACHE_SETS; nSet++) {
for (nWay = 0; nWay < L1_DATA_CACHE_WAYS; nWay++) {
nSetWayLevel = nWay << L1_SETWAY_WAY_SHIFT
| nSet << L1_SETWAY_SET_SHIFT
| 0 << SETWAY_LEVEL_SHIFT;
asm volatile ("mcr p15, 0, %0, c7, c10, 2" : : "r" (nSetWayLevel) : "memory");
}
}
// clean L2 unified cache
for (nSet = 0; nSet < L2_CACHE_SETS; nSet++) {
for (nWay = 0; nWay < L2_CACHE_WAYS; nWay++) {
nSetWayLevel = nWay << L2_SETWAY_WAY_SHIFT
| nSet << L2_SETWAY_SET_SHIFT
| 1 << SETWAY_LEVEL_SHIFT;
asm volatile ("mcr p15, 0, %0, c7, c10, 2" : : "r" (nSetWayLevel) : "memory");
}
}
}
#endif
// TLB 4KB Section Descriptor format
// 31..12 Section Base Address
// 11..9 - unused, set to zero
// 8..6 TEX - type extension- TEX, C, B used together, see below
// 5..4 AP - access ctrl - set to 11 for full access from user and super modes
// 3 C - cacheable - TEX, C, B used together, see below
// 2 B - bufferable - TEX, C, B used together, see below
// 1 1
// 0 1
void map_4k_page(int logical, int physical) {
// Invalidate the data TLB before changing mapping
_invalidate_dtlb_mva((void *)(logical << 12));
// Setup the 4K page table entry
// Second level descriptors use extended small page format so inner/outer cacheing can be controlled
// Pi 0/1:
// XP (bit 23) in SCTRL is 0 so descriptors use ARMv4/5 backwards compatible format
// Pi 2/3:
// XP (bit 23) in SCTRL no longer exists, and we see to be using ARMv6 table formats
// this means bit 0 of the page table is actually XN and must be clear to allow native ARM code to execute
// (this was the cause of issue #27)
#if defined(RPI2) || defined (RPI3)
PageTable2[logical] = (physical<<12) | 0x132 | (bb << 6) | (aa << 2);
#else
PageTable2[logical] = (physical<<12) | 0x133 | (bb << 6) | (aa << 2);
#endif
}
void enable_MMU_and_IDCaches(void)
{
//DEBUG_LOG("enable_MMU_and_IDCaches\r\n");
//DEBUG_LOG("cpsr = %08x\r\n", _get_cpsr());
unsigned i;
unsigned base;
// TLB 1MB Sector Descriptor format
// 31..20 Section Base Address
// 19 NS - ? - set to 0
// 18 0 - - set to 0
// 17 nG - ? - set to 0
// 16 S - ? - set to 0
// 15 APX - access ctrl - set to 0 for full access from user and super modes
// 14..12 TEX - type extension- TEX, C, B used together, see below
// 11..10 AP - access ctrl - set to 11 for full access from user and super modes
// 9 P - - set to 0
// 8..5 Domain- access domain - set to 0000 as nor using access ctrl
// 4 XN - eXecute Never - set to 1 for I/O devices
// 3 C - cacheable - set to 1 for cachable RAM i
// 2 B - bufferable - set to 1 for cachable RAM
// 1 1 - TEX, C, B used together, see below
// 0 0 - TEX, C, B used together, see below
// For I/O devices
// TEX = 000; C=0; B=1 (Shared device)
// For cacheable RAM
// TEX = 001; C=1; B=1 (Outer and inner write back, write allocate)
// For non-cachable RAM
// TEX = 001; C=0; B=0 (Outer and inner non-cacheable)
// For individual control
// TEX = 1BB CB=AA
// AA = inner policy
// BB = outer policy
// 00 = NC (non-cacheable)
// 01 = WBWA (write-back, write allocate)
// 10 = WT (write-through
// 11 = WBNWA (write-back, no write allocate)
/// TEX = 100; C=0; B=1 (outer non cacheable, inner write-back, write allocate)
for (base = 0; base < l1_cached_threshold; base++)
{
// Value from my original RPI code = 11C0E (outer and inner write back, write allocate, shareable)
// bits 11..10 are the AP bits, and setting them to 11 enables user mode access as well
// Values from RPI2 = 11C0E (outer and inner write back, write allocate, shareable (fast but unsafe)); works on RPI
// Values from RPI2 = 10C0A (outer and inner write through, no write allocate, shareable)
// Values from RPI2 = 15C0A (outer write back, write allocate, inner write through, no write allocate, shareable)
PageTable[base] = base << 20 | 0x04C02 | (shareable << 16) | (bb << 12) | (aa << 2);
}
for (; base < l2_cached_threshold; base++)
{
PageTable[base] = base << 20 | 0x04C02 | (shareable << 16) | (bb << 12);
}
for (; base < uncached_threshold; base++)
{
PageTable[base] = base << 20 | 0x01C02;
}
for (; base < 4096; base++)
{
// shared device, never execute
PageTable[base] = base << 20 | 0x10C16;
}
// suppress a warning as we really do want to copy from src address 0!
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
// copy vectors from virtual address zero to a higher unused location
memcpy((void *)HIGH_VECTORS_BASE, (void *)0, 0x1000);
#pragma GCC diagnostic pop
// replace the first N 1MB entries with second level page tables, giving N x 256 4K pages
for (i = 0; i < NUM_4K_PAGES >> 8; i++)
{
PageTable[i] = (unsigned int) (&PageTable2[i << 8]);
PageTable[i] +=1;
}
// populate the second level page tables
for (base = 0; base < NUM_4K_PAGES; base++)
{
map_4k_page(base, base);
}
// relocate the vector pointer to the moved page
asm volatile("mcr p15, 0, %[addr], c12, c0, 0" : : [addr] "r" (HIGH_VECTORS_BASE));
#if defined(RPI3)
unsigned cpuextctrl0, cpuextctrl1;
asm volatile ("mrrc p15, 1, %0, %1, c15" : "=r" (cpuextctrl0), "=r" (cpuextctrl1));
//DEBUG_LOG("extctrl = %08x %08x\r\n", cpuextctrl1, cpuextctrl0);
#else
// RPI: bit 6 of auxctrl is restrict cache size to 16K (no page coloring)
// RPI2: bit 6 of auxctrl is set SMP bit, otherwise all caching disabled
unsigned auxctrl;
asm volatile ("mrc p15, 0, %0, c1, c0, 1" : "=r" (auxctrl));
auxctrl |= 1 << 6;
asm volatile ("mcr p15, 0, %0, c1, c0, 1" :: "r" (auxctrl));
asm volatile ("mrc p15, 0, %0, c1, c0, 1" : "=r" (auxctrl));
//DEBUG_LOG("auxctrl = %08x\r\n", auxctrl);
#endif
// set domain 0 to client
asm volatile ("mcr p15, 0, %0, c3, c0, 0" :: "r" (1));
// always use TTBR0
asm volatile ("mcr p15, 0, %0, c2, c0, 2" :: "r" (0));
unsigned ttbcr;
asm volatile ("mrc p15, 0, %0, c2, c0, 2" : "=r" (ttbcr));
//DEBUG_LOG("ttbcr = %08x\r\n", ttbcr);
#if defined(RPI2) || defined(RPI3)
// set TTBR0 - page table walk memory cacheability/shareable
// [Bit 0, Bit 6] indicates inner cachability: 01 = normal memory, inner write-back write-allocate cacheable
// [Bit 4, Bit 3] indicates outer cachability: 01 = normal memory, outer write-back write-allocate cacheable
// Bit 1 indicates sharable
// 4A = 0100 1010
int attr = ((aa & 1) << 6) | (bb << 3) | (shareable << 1) | ((aa & 2) >> 1);
asm volatile ("mcr p15, 0, %0, c2, c0, 0" :: "r" (attr | (unsigned) &PageTable));
#else
// set TTBR0 (page table walk inner cacheable, outer non-cacheable, shareable memory)
asm volatile ("mcr p15, 0, %0, c2, c0, 0" :: "r" (0x03 | (unsigned) &PageTable));
#endif
unsigned ttbr0;
asm volatile ("mrc p15, 0, %0, c2, c0, 0" : "=r" (ttbr0));
//DEBUG_LOG("ttbr0 = %08x\r\n", ttbr0);
// Invalidate entire data cache
#if defined(RPI2) || defined(RPI3)
asm volatile ("isb" ::: "memory");
InvalidateDataCache();
#else
// invalidate data cache and flush prefetch buffer
// NOTE: The below code seems to cause a Pi 2 to crash
asm volatile ("mcr p15, 0, %0, c7, c5, 4" :: "r" (0) : "memory");
asm volatile ("mcr p15, 0, %0, c7, c6, 0" :: "r" (0) : "memory");
#endif
// enable MMU, L1 cache and instruction cache, L2 cache, write buffer,
// branch prediction and extended page table on
unsigned sctrl;
asm volatile ("mrc p15,0,%0,c1,c0,0" : "=r" (sctrl));
// Bit 13 enable vector relocation
// Bit 12 enables the L1 instruction cache
// Bit 11 enables branch pre-fetching
// Bit 2 enables the L1 data cache
// Bit 0 enabled the MMU
// The L1 instruction cache can be used independently of the MMU
// The L1 data cache will one be enabled if the MMU is enabled
sctrl |= 0x00001805;
asm volatile ("mcr p15,0,%0,c1,c0,0" :: "r" (sctrl) : "memory");
asm volatile ("mrc p15,0,%0,c1,c0,0" : "=r" (sctrl));
//DEBUG_LOG("sctrl = %08x\r\n", sctrl);
// For information, show the cache type register
// From this you can tell what type of cache is implemented
unsigned ctype;
asm volatile ("mrc p15,0,%0,c0,c0,1" : "=r" (ctype));
//DEBUG_LOG("ctype = %08x\r\n", ctype);
}

32
cache.h Normal file
View File

@ -0,0 +1,32 @@
// Part of PiTubeDirect
// https://github.com/hoglet67/PiTubeDirect
// cache.h
#ifndef CACHE_H
#define CACHE_H
// Memory below 64MB is L1 and L2 cached (inner and outer)
// Mark the memory above 64MB to 128MB as L2 cached only (outer)
#define L2_CACHED_MEM_BASE 0x04000000
// Mark the memory above 128MB as uncachable
#define UNCACHED_MEM_BASE 0x08000000
// Location of the high vectors (last page of L1 cached memory)
#define HIGH_VECTORS_BASE (L2_CACHED_MEM_BASE - 0x1000)
// The first 2MB of memory is mapped at 4K pages so the 6502 Co Pro
// can play tricks with banks selection
#define NUM_4K_PAGES 512
#ifndef __ASSEMBLER__
void map_4k_page(int logical, int physical);
void enable_MMU_and_IDCaches(void);
#endif
#endif

16
debug.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef DEBUG_H
#define DEBUG_H
#ifdef __ASSEMBLER__
#else
#include <stdio.h>
#endif
#ifdef DEBUG
#define DEBUG_LOG(...) printf(__VA_ARGS__)
#else
#define DEBUG_LOG(...)
#endif
#endif

71
defs.h Normal file
View File

@ -0,0 +1,71 @@
#ifndef DEFS_H
#define DEFS_H
#include "debug.h"
// Indicates a Pi with the 40 pin GPIO connector
// so that additional functionality (e.g. test pins) can be enabled
#if defined(RPIZERO) || defined(RPIBPLUS) || defined(RPI2) || defined(RPI3)
#define HAS_40PINS
#endif
// Pi 2/3 Multicore options
#if defined(RPI2) || defined(RPI3)
// Indicate the platform has multiple cores
#define HAS_MULTICORE
#define USE_GPU
#define USE_HW_MAILBOX
// Indicates we want to make active use of multiple cores
#define USE_MULTICORE
// Needs to match kernel_old setting in config.txt
//#define KERNEL_OLD
// Include instruction histogram in multi core 65tube
//#define HISTOGRAM
#else
#define USE_GPU
#define USE_HW_MAILBOX
#endif
#include "rpi-base.h"
#ifdef USE_HW_MAILBOX
#define MBOX0_READ (PERIPHERAL_BASE + 0x00B880)
#define MBOX0_STATUS (PERIPHERAL_BASE + 0x00B898)
#define MBOX0_CONFIG (PERIPHERAL_BASE + 0x00B89C)
#define MBOX0_EMPTY (0x40000000)
#define MBOX0_DATAIRQEN (0x00000001)
#endif
#ifdef __ASSEMBLER__
#define GPFSEL0 (PERIPHERAL_BASE + 0x200000) // controls GPIOs 0..9
#define GPFSEL1 (PERIPHERAL_BASE + 0x200004) // controls GPIOs 10..19
#define GPFSEL2 (PERIPHERAL_BASE + 0x200008) // controls GPIOs 20..29
#define GPSET0 (PERIPHERAL_BASE + 0x20001C)
#define GPCLR0 (PERIPHERAL_BASE + 0x200028)
#define GPLEV0 (PERIPHERAL_BASE + 0x200034)
#define GPEDS0 (PERIPHERAL_BASE + 0x200040)
#define FIQCTRL (PERIPHERAL_BASE + 0x00B20C)
#endif // __ASSEMBLER__
#ifdef HAS_40PINS
#define TEST_PIN (21)
#define TEST_MASK (1 << TEST_PIN)
#define TEST2_PIN (20)
#define TEST2_MASK (1 << TEST2_PIN)
#define TEST3_PIN (16)
#define TEST3_MASK (1 << TEST3_PIN)
#endif
#endif

275
diskio.cpp Normal file
View File

@ -0,0 +1,275 @@
/*-----------------------------------------------------------------------*/
/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be */
/* attached to the FatFs via a glue function rather than modifying it. */
/* This is an example of glue functions to attach various exsisting */
/* storage control modules to the FatFs module with a defined API. */
/*-----------------------------------------------------------------------*/
#include "diskio.h" /* FatFs lower layer API */
/* Definitions of physical drive number for each drive */
#define DEV_MMC 0 /* Example: Map MMC/SD card to physical drive 0 */
//static struct emmc_block_dev *emmc_dev;
static CEMMCDevice* pEMMC;
#define SD_BLOCK_SIZE 512
void disk_setEMM(CEMMCDevice* pEMMCDevice)
{
pEMMC = pEMMCDevice;
}
int sd_card_init(struct block_device **dev)
{
return 0;
}
size_t sd_read(uint8_t *buf, size_t buf_size, uint32_t block_no)
{
// g_pLogger->Write("", LogNotice, "sd_read %d", block_no);
return pEMMC->DoRead(buf, buf_size, block_no);
}
size_t sd_write(uint8_t *buf, size_t buf_size, uint32_t block_no)
{
return pEMMC->DoWrite(buf, buf_size, block_no);
}
/*-----------------------------------------------------------------------*/
/* Get Drive Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
//DSTATUS stat;
//int result;
//switch (pdrv) {
////case DEV_RAM :
//// result = RAM_disk_status();
//// // translate the reslut code here
//// return stat;
//case DEV_MMC :
// result = MMC_disk_status();
// // translate the reslut code here
// return stat;
////case DEV_USB :
//// result = USB_disk_status();
//// // translate the reslut code here
//// return stat;
//}
//return STA_NOINIT;
return 0;
}
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
//DSTATUS stat;
//int result;
//switch (pdrv) {
////case DEV_RAM :
//// result = RAM_disk_initialize();
//// // translate the reslut code here
//// return stat;
////case DEV_MMC :
//// result = MMC_disk_initialize();
//// // translate the reslut code here
//// return stat;
////case DEV_USB :
//// result = USB_disk_initialize();
//// // translate the reslut code here
//// return stat;
//}
//return STA_NOINIT;
return 0;
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Start sector in LBA */
UINT count /* Number of sectors to read */
)
{
//DRESULT res;
//int result;
// g_pLogger->Write("", LogNotice, "disk_read pdrv = %d", pdrv);
switch (pdrv) {
//case DEV_RAM :
// // translate the arguments here
// result = RAM_disk_read(buff, sector, count);
// // translate the reslut code here
// return res;
case DEV_MMC :
// translate the arguments here
//result = MMC_disk_read(buff, sector, count);
// g_pLogger->Write("", LogNotice, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!disk_read %d %d buf_size = 0x%x", sector, count, buf_size);
for (UINT s = 0; s < count; ++s)
{
if (sd_read(buff, SD_BLOCK_SIZE, sector+s) < SD_BLOCK_SIZE)
{
return RES_ERROR;
}
buff += SD_BLOCK_SIZE;
}
return RES_OK;
//case DEV_USB :
// // translate the arguments here
// result = USB_disk_read(buff, sector, count);
// // translate the reslut code here
// return res;
}
return RES_PARERR;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Start sector in LBA */
UINT count /* Number of sectors to write */
)
{
//DRESULT res;
//int result;
switch (pdrv) {
//case DEV_RAM :
// // translate the arguments here
// result = RAM_disk_write(buff, sector, count);
// // translate the reslut code here
// return res;
case DEV_MMC :
// translate the arguments here
//result = MMC_disk_write(buff, sector, count);
//size_t buf_size = count * SD_BLOCK_SIZE;
//if (sd_write((uint8_t *)buff, buf_size, sector) < buf_size)
//{
// return RES_ERROR;
//}
for (UINT s = 0; s < count; ++s)
{
if (sd_write((uint8_t *)buff, SD_BLOCK_SIZE, sector+s) < SD_BLOCK_SIZE)
{
return RES_ERROR;
}
buff += SD_BLOCK_SIZE;
}
return RES_OK;
//case DEV_USB :
// // translate the arguments here
// result = USB_disk_write(buff, sector, count);
// // translate the reslut code here
// return res;
}
return RES_PARERR;
}
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
//DRESULT res;
//int result;
//switch (pdrv) {
//case DEV_RAM :
// // Process of the command for the RAM drive
// return res;
//case DEV_MMC :
// // Process of the command for the MMC/SD card
// return res;
//case DEV_USB :
// // Process of the command the USB drive
// return res;
//}
return RES_PARERR;
}

83
diskio.h Normal file
View File

@ -0,0 +1,83 @@
/*-----------------------------------------------------------------------/
/ Low level disk interface modlue include file (C)ChaN, 2014 /
/-----------------------------------------------------------------------*/
#ifndef _DISKIO_DEFINED
#define _DISKIO_DEFINED
#include "integer.h"
#include "emmc.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Status of Disk Functions */
typedef BYTE DSTATUS;
/* Results of Disk Functions */
typedef enum {
RES_OK = 0, /* 0: Successful */
RES_ERROR, /* 1: R/W Error */
RES_WRPRT, /* 2: Write Protected */
RES_NOTRDY, /* 3: Not Ready */
RES_PARERR /* 4: Invalid Parameter */
} DRESULT;
/*---------------------------------------*/
/* Prototypes for disk control functions */
void disk_setEMM(CEMMCDevice* pEMMCDevice);
DSTATUS disk_initialize (BYTE pdrv);
DSTATUS disk_status (BYTE pdrv);
DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
/* Disk Status Bits (DSTATUS) */
#define STA_NOINIT 0x01 /* Drive not initialized */
#define STA_NODISK 0x02 /* No medium in the drive */
#define STA_PROTECT 0x04 /* Write protected */
/* Command code for disk_ioctrl fucntion */
/* Generic command (Used by FatFs) */
#define CTRL_SYNC 0 /* Complete pending write process (needed at _FS_READONLY == 0) */
#define GET_SECTOR_COUNT 1 /* Get media size (needed at _USE_MKFS == 1) */
#define GET_SECTOR_SIZE 2 /* Get sector size (needed at _MAX_SS != _MIN_SS) */
#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at _USE_MKFS == 1) */
#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */
/* Generic command (Not used by FatFs) */
#define CTRL_POWER 5 /* Get/Set power status */
#define CTRL_LOCK 6 /* Lock/Unlock media removal */
#define CTRL_EJECT 7 /* Eject media */
#define CTRL_FORMAT 8 /* Create physical format on the media */
/* MMC/SDC specific ioctl command */
#define MMC_GET_TYPE 10 /* Get card type */
#define MMC_GET_CSD 11 /* Get CSD */
#define MMC_GET_CID 12 /* Get CID */
#define MMC_GET_OCR 13 /* Get OCR */
#define MMC_GET_SDSTAT 14 /* Get SD status */
#define ISDIO_READ 55 /* Read data form SD iSDIO register */
#define ISDIO_WRITE 56 /* Write data to SD iSDIO register */
#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */
/* ATA/CF specific ioctl command */
#define ATA_GET_REV 20 /* Get F/W revision */
#define ATA_GET_MODEL 21 /* Get model name */
#define ATA_GET_SN 22 /* Get serial number */
#ifdef __cplusplus
}
#endif
#endif

1998
emmc.cpp Normal file

File diff suppressed because it is too large Load Diff

108
emmc.h Normal file
View File

@ -0,0 +1,108 @@
#ifndef _SDCard_emmc_h
#define _SDCard_emmc_h
#define PACKED __attribute__ ((packed))
#include "types.h"
/*
#include <circle/device.h>
//#include <circle/interrupt.h>
#include <circle/timer.h>
#include <circle/actled.h>
#include <circle/fs/partitionmanager.h>
#include <circle/logger.h>
#include <circle/types.h>
*/
#define BOOT_SIGNATURE 0xAA55
struct TSCR // SD configuration register
{
u32 scr[2];
u32 sd_bus_widths;
int sd_version;
};
class CEMMCDevice
{
public:
CEMMCDevice();
~CEMMCDevice();
bool Initialize(void);
int Read(void *pBuffer, unsigned nCount);
int Write(const void *pBuffer, unsigned nCount);
unsigned long long Seek(unsigned long long ullOffset);
int DoRead2(u8 *buf, size_t buf_size, u32 block_no);
int DoRead(u8 *buf, size_t buf_size, u32 block_no);
int DoWrite(u8 *buf, size_t buf_size, u32 block_no);
private:
bool PowerOn(void);
void PowerOff(void);
u32 GetBaseClock(void);
u32 GetClockDivider(u32 base_clock, u32 target_rate);
int SwitchClockRate(u32 base_clock, u32 target_rate);
int ResetCmd(void);
int ResetDat(void);
void IssueCommandInt(u32 cmd_reg, u32 argument, int timeout);
void HandleCardInterrupt(void);
void HandleInterrupts(void);
bool IssueCommand(u32 command, u32 argument, int timeout = 500000);
int CardReset(void);
int CardInit(void);
int EnsureDataMode(void);
int DoDataCommand(int is_write, u8 *buf, size_t buf_size, u32 block_no);
int TimeoutWait(unsigned reg, unsigned mask, int value, unsigned usec);
void usDelay(unsigned usec);
private:
u64 m_ullOffset;
u32 m_hci_ver;
// was: struct emmc_block_dev
u32 m_device_id[4];
u32 m_card_supports_sdhc;
u32 m_card_supports_18v;
u32 m_card_ocr;
u32 m_card_rca;
u32 m_last_interrupt;
u32 m_last_error;
TSCR m_SCR;
int m_failed_voltage_switch;
u32 m_last_cmd_reg;
u32 m_last_cmd;
u32 m_last_cmd_success;
u32 m_last_r0;
u32 m_last_r1;
u32 m_last_r2;
u32 m_last_r3;
void *m_buf;
int m_blocks_to_transfer;
size_t m_block_size;
int m_card_removal;
u32 m_base_clock;
static const char *sd_versions[];
static const char *err_irpts[];
static const u32 sd_commands[];
static const u32 sd_acommands[];
};
#endif

179
exception.c Normal file
View File

@ -0,0 +1,179 @@
#include "rpi-base.h"
#include "rpi-aux.h"
#include "rpi-gpio.h"
#include "rpi-interrupts.h"
#include "defs.h"
#include "startup.h"
// From here: https://www.raspberrypi.org/forums/viewtopic.php?f=72&t=53862
void reboot_now(void)
{
const int PM_PASSWORD = 0x5a000000;
const int PM_RSTC_WRCFG_FULL_RESET = 0x00000020;
unsigned int *PM_WDOG = (unsigned int *) (PERIPHERAL_BASE + 0x00100024);
unsigned int *PM_RSTC = (unsigned int *) (PERIPHERAL_BASE + 0x0010001c);
// timeout = 1/16th of a second? (whatever)
*PM_WDOG = PM_PASSWORD | 1;
*PM_RSTC = PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET;
while(1);
}
void dump_digit(unsigned int c) {
c &= 15;
if (c < 10) {
c = '0' + c;
} else {
c = 'A' + c - 10;
}
RPI_AuxMiniUartWrite(c);
}
void dump_hex(unsigned int value) {
int i;
for (i = 0; i < 8; i++) {
int c = value >> 28;
if (c < 10) {
c = '0' + c;
} else {
c = 'A' + c - 10;
}
RPI_AuxMiniUartWrite(c);
value <<= 4;
}
}
void dump_binary(unsigned int value) {
int i;
for (i = 0; i < 32; i++) {
int c = '0' + (value >> 31);
RPI_AuxMiniUartWrite(c);
value <<= 1;
}
}
void dump_string(char *string) {
char c;
while ((c = *string++) != 0) {
RPI_AuxMiniUartWrite(c);
}
}
// For some reason printf generally doesn't work here
void dump_info(unsigned int *context, int offset, char *type) {
unsigned int *addr;
unsigned int *reg;
unsigned int flags;
int i, j;
//int rstlow;
//int led;
// Make sure we avoid unaligned accesses
context = (unsigned int *)(((unsigned int) context) & ~3);
// context point into the exception stack, at flags, followed by registers 0 .. 13
reg = context + 1;
dump_string(type);
dump_string(" at ");
// The stacked LR points one or two words afer the exception address
addr = (unsigned int *)((reg[13] & ~3) - offset);
dump_hex((unsigned int)addr);
#ifdef HAS_MULTICORE
//dump_string(" on core ");
//dump_digit(_get_core());
#endif
dump_string("\r\n");
dump_string("Registers:\r\n");
for (i = 0; i <= 13; i++) {
j = (i < 13) ? i : 14; // slot 13 actually holds the link register
dump_string(" r[");
RPI_AuxMiniUartWrite('0' + (j / 10));
RPI_AuxMiniUartWrite('0' + (j % 10));
dump_string("]=");
dump_hex(reg[i]);
dump_string("\r\n");
}
dump_string("Memory:\r\n");
for (i = -4; i <= 4; i++) {
dump_string(" ");
dump_hex((unsigned int) (addr + i));
RPI_AuxMiniUartWrite('=');
dump_hex(*(addr + i));
if (i == 0) {
dump_string(" <<<<<< \r\n");
} else {
dump_string("\r\n");
}
}
// The flags are pointed to by context, before the registers
flags = *context;
dump_string("Flags: \r\n NZCV--------------------IFTMMMMM\r\n ");
dump_binary(flags);
dump_string(" (");
// The last 5 bits of the flags are the mode
switch (flags & 0x1f) {
case 0x10:
dump_string("User");
break;
case 0x11:
dump_string("FIQ");
break;
case 0x12:
dump_string("IRQ");
break;
case 0x13:
dump_string("Supervisor");
break;
case 0x17:
dump_string("Abort");
break;
case 0x1B:
dump_string("Undefined");
break;
case 0x1F:
dump_string("System");
break;
default:
dump_string("Illegal");
break;
};
dump_string(" Mode)\r\n");
dump_string("Halted waiting for reset\r\n");
//rstlow = 0;
//led = 0;
// while (1) {
//for (i = 0; i < 1000000; i++) {
// // look for reset being low
// if (tube_is_rst_active()) {
// rstlow = 1;
// }
// // then reset on the next rising edge
// if (rstlow && (!tube_is_rst_active())) {
// reboot_now();
// }
//}
//if (led) {
// LED_OFF();
//} else {
// LED_ON();
//}
//led = ~led;
// }
}
void undefined_instruction_handler(unsigned int *context) {
dump_info(context, 4, "Undefined Instruction");
}
void prefetch_abort_handler(unsigned int *context) {
dump_info(context, 4, "Prefetch Abort");
}
void data_abort_handler(unsigned int *context) {
dump_info(context, 8, "Data Abort");
}
void swi_handler(unsigned int *context) {
dump_info(context, 4, "SWI");
}

6052
ff.cpp Normal file

File diff suppressed because it is too large Load Diff

366
ff.h Normal file
View File

@ -0,0 +1,366 @@
/*----------------------------------------------------------------------------/
/ FatFs - Generic FAT file system module R0.12b /
/-----------------------------------------------------------------------------/
/
/ Copyright (C) 2016, ChaN, all right reserved.
/
/ FatFs module is an open source software. Redistribution and use of FatFs in
/ source and binary forms, with or without modification, are permitted provided
/ that the following condition is met:
/ 1. Redistributions of source code must retain the above copyright notice,
/ this condition and the following disclaimer.
/
/ This software is provided by the copyright holder and contributors "AS IS"
/ and any warranties related to this software are DISCLAIMED.
/ The copyright owner or contributors be NOT LIABLE for any damages caused
/ by use of this software.
/----------------------------------------------------------------------------*/
#ifndef _FATFS
#define _FATFS 68020 /* Revision ID */
#ifdef __cplusplus
extern "C" {
#endif
#include "integer.h" /* Basic integer types */
#include "ffconf.h" /* FatFs configuration options */
#if _FATFS != _FFCONF
#error Wrong configuration file (ffconf.h).
#endif
/* Definitions of volume management */
#if _MULTI_PARTITION /* Multiple partition configuration */
typedef struct {
BYTE pd; /* Physical drive number */
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
} PARTITION;
extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive number */
#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition index */
#else /* Single partition configuration */
#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */
#define LD2PT(vol) 0 /* Find first valid partition or in SFD */
#endif
/* Type of path name strings on FatFs API */
#if _LFN_UNICODE /* Unicode (UTF-16) string */
#if _USE_LFN == 0
#error _LFN_UNICODE must be 0 at non-LFN cfg.
#endif
#ifndef _INC_TCHAR
typedef WCHAR TCHAR;
#define _T(x) L ## x
#define _TEXT(x) L ## x
#endif
#else /* ANSI/OEM string */
#ifndef _INC_TCHAR
typedef char TCHAR;
#define _T(x) x
#define _TEXT(x) x
#endif
#endif
/* Type of file size variables */
#if _FS_EXFAT
#if _USE_LFN == 0
#error LFN must be enabled when enable exFAT
#endif
typedef QWORD FSIZE_t;
#else
typedef DWORD FSIZE_t;
#endif
/* File system object structure (FATFS) */
typedef struct {
BYTE fs_type; /* File system type (0:N/A) */
BYTE drv; /* Physical drive number */
BYTE n_fats; /* Number of FATs (1 or 2) */
BYTE wflag; /* win[] flag (b0:dirty) */
BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
WORD id; /* File system mount ID */
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
WORD csize; /* Cluster size [sectors] */
#if _MAX_SS != _MIN_SS
WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
#endif
#if _USE_LFN != 0
WCHAR* lfnbuf; /* LFN working buffer */
#endif
#if _FS_EXFAT
BYTE* dirbuf; /* Directory entry block scratchpad buffer */
#endif
#if _FS_REENTRANT
_SYNC_t sobj; /* Identifier of sync object */
#endif
#if !_FS_READONLY
DWORD last_clst; /* Last allocated cluster */
DWORD free_clst; /* Number of free clusters */
#endif
#if _FS_RPATH != 0
DWORD cdir; /* Current directory start cluster (0:root) */
#if _FS_EXFAT
DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */
DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */
DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */
#endif
#endif
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
DWORD fsize; /* Size of an FAT [sectors] */
DWORD volbase; /* Volume base sector */
DWORD fatbase; /* FAT base sector */
DWORD dirbase; /* Root directory base sector/cluster */
DWORD database; /* Data base sector */
DWORD winsect; /* Current sector appearing in the win[] */
BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
} FATFS;
/* Object ID and allocation information (_FDID) */
typedef struct {
FATFS* fs; /* Pointer to the owner file system object */
WORD id; /* Owner file system mount ID */
BYTE attr; /* Object attribute */
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous (no data on FAT), =3:got flagmented, b2:sub-directory stretched) */
DWORD sclust; /* Object start cluster (0:no cluster or root directory) */
FSIZE_t objsize; /* Object size (valid when sclust != 0) */
#if _FS_EXFAT
DWORD n_cont; /* Size of coutiguous part, clusters - 1 (valid when stat == 3) */
DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */
DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
DWORD c_ofs; /* Offset in the containing directory (valid when sclust != 0) */
#endif
#if _FS_LOCK != 0
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
#endif
} _FDID;
/* File object structure (FIL) */
typedef struct {
_FDID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
BYTE flag; /* File status flags */
BYTE err; /* Abort flag (error code) */
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
DWORD clust; /* Current cluster of fpter (invalid when fprt is 0) */
DWORD sect; /* Sector number appearing in buf[] (0:invalid) */
#if !_FS_READONLY
DWORD dir_sect; /* Sector number containing the directory entry */
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */
#endif
#if _USE_FASTSEEK
DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */
#endif
#if !_FS_TINY
BYTE buf[_MAX_SS]; /* File private data read/write window */
#endif
} FIL;
/* Directory object structure (DIR) */
typedef struct {
_FDID obj; /* Object identifier */
DWORD dptr; /* Current read/write offset */
DWORD clust; /* Current cluster */
DWORD sect; /* Current sector */
BYTE* dir; /* Pointer to the directory item in the win[] */
BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
#if _USE_LFN != 0
DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
#endif
#if _USE_FIND
const TCHAR* pat; /* Pointer to the name matching pattern */
#endif
} DIR;
/* File information structure (FILINFO) */
typedef struct {
FSIZE_t fsize; /* File size */
WORD fdate; /* Modified date */
WORD ftime; /* Modified time */
BYTE fattrib; /* File attribute */
#if _USE_LFN != 0
TCHAR altname[13]; /* Altenative file name */
TCHAR fname[_MAX_LFN + 1]; /* Primary file name */
#else
TCHAR fname[13]; /* File name */
#endif
} FILINFO;
/* File function return code (FRESULT) */
typedef enum {
FR_OK = 0, /* (0) Succeeded */
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
FR_INT_ERR, /* (2) Assertion failed */
FR_NOT_READY, /* (3) The physical drive cannot work */
FR_NO_FILE, /* (4) Could not find the file */
FR_NO_PATH, /* (5) Could not find the path */
FR_INVALID_NAME, /* (6) The path name format is invalid */
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
FR_EXIST, /* (8) Access denied due to prohibited access */
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
FR_NOT_ENABLED, /* (12) The volume has no work area */
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_LOCK */
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
} FRESULT;
/*--------------------------------------------------------------*/
/* FatFs module application interface */
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
FRESULT f_close (FIL* fp); /* Close an open file object */
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
FRESULT f_truncate (FIL* fp); /* Truncate the file */
FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */
FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */
FRESULT f_closedir (DIR* dp); /* Close an open directory */
FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */
FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */
FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
#define f_error(fp) ((fp)->err)
#define f_tell(fp) ((fp)->fptr)
#define f_size(fp) ((fp)->obj.objsize)
#define f_rewind(fp) f_lseek((fp), 0)
#define f_rewinddir(dp) f_readdir((dp), 0)
#ifndef EOF
#define EOF (-1)
#endif
/*--------------------------------------------------------------*/
/* Additional user defined functions */
/* RTC function */
#if !_FS_READONLY && !_FS_NORTC
DWORD get_fattime (void);
#endif
/* Unicode support functions */
#if _USE_LFN != 0 /* Unicode - OEM code conversion */
WCHAR ff_convert (WCHAR chr, UINT dir); /* OEM-Unicode bidirectional conversion */
WCHAR ff_wtoupper (WCHAR chr); /* Unicode upper-case conversion */
#if _USE_LFN == 3 /* Memory functions */
void* ff_memalloc (UINT msize); /* Allocate memory block */
void ff_memfree (void* mblock); /* Free memory block */
#endif
#endif
/* Sync functions */
#if _FS_REENTRANT
int ff_cre_syncobj (BYTE vol, _SYNC_t* sobj); /* Create a sync object */
int ff_req_grant (_SYNC_t sobj); /* Lock sync object */
void ff_rel_grant (_SYNC_t sobj); /* Unlock sync object */
int ff_del_syncobj (_SYNC_t sobj); /* Delete a sync object */
#endif
/*--------------------------------------------------------------*/
/* Flags and offset address */
/* File access mode and open method flags (3rd argument of f_open) */
#define FA_READ 0x01
#define FA_WRITE 0x02
#define FA_OPEN_EXISTING 0x00
#define FA_CREATE_NEW 0x04
#define FA_CREATE_ALWAYS 0x08
#define FA_OPEN_ALWAYS 0x10
#define FA_OPEN_APPEND 0x30
/* Fast seek controls (2nd argument of f_lseek) */
#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
/* Format options (2nd argument of f_mkfs) */
#define FM_FAT 0x01
#define FM_FAT32 0x02
#define FM_EXFAT 0x04
#define FM_ANY 0x07
#define FM_SFD 0x08
/* Filesystem type (FATFS.fs_type) */
#define FS_FAT12 1
#define FS_FAT16 2
#define FS_FAT32 3
#define FS_EXFAT 4
/* File attribute bits for directory entry (FILINFO.fattrib) */
#define AM_RDO 0x01 /* Read only */
#define AM_HID 0x02 /* Hidden */
#define AM_SYS 0x04 /* System */
#define AM_DIR 0x10 /* Directory */
#define AM_ARC 0x20 /* Archive */
#ifdef __cplusplus
}
#endif
#endif /* _FATFS */

267
ffconf.h Normal file
View File

@ -0,0 +1,267 @@
/*---------------------------------------------------------------------------/
/ FatFs - FAT file system module configuration file
/---------------------------------------------------------------------------*/
#define _FFCONF 68020 /* Revision ID */
/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/
#define _FS_READONLY 0
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
/ and optional writing functions as well. */
#define _FS_MINIMIZE 0
/* This option defines minimization level to remove some basic API functions.
/
/ 0: All basic functions are enabled.
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
/ are removed.
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
/ 3: f_lseek() function is removed in addition to 2. */
#define _USE_STRFUNC 0
/* This option switches string functions, f_gets(), f_putc(), f_puts() and
/ f_printf().
/
/ 0: Disable string functions.
/ 1: Enable without LF-CRLF conversion.
/ 2: Enable with LF-CRLF conversion. */
#define _USE_FIND 1
/* This option switches filtered directory read functions, f_findfirst() and
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
#define _USE_MKFS 0
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
#define _USE_FASTSEEK 0
/* This option switches fast seek function. (0:Disable or 1:Enable) */
#define _USE_EXPAND 0
/* This option switches f_expand function. (0:Disable or 1:Enable) */
#define _USE_CHMOD 1
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
/ (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */
#define _USE_LABEL 0
/* This option switches volume label functions, f_getlabel() and f_setlabel().
/ (0:Disable or 1:Enable) */
#define _USE_FORWARD 0
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/
#define _CODE_PAGE 437
/* This option specifies the OEM code page to be used on the target system.
/ Incorrect setting of the code page can cause a file open failure.
/
/ 1 - ASCII (No extended character. Non-LFN cfg. only)
/ 437 - U.S.
/ 720 - Arabic
/ 737 - Greek
/ 771 - KBL
/ 775 - Baltic
/ 850 - Latin 1
/ 852 - Latin 2
/ 855 - Cyrillic
/ 857 - Turkish
/ 860 - Portuguese
/ 861 - Icelandic
/ 862 - Hebrew
/ 863 - Canadian French
/ 864 - Arabic
/ 865 - Nordic
/ 866 - Russian
/ 869 - Greek 2
/ 932 - Japanese (DBCS)
/ 936 - Simplified Chinese (DBCS)
/ 949 - Korean (DBCS)
/ 950 - Traditional Chinese (DBCS)
*/
#define _USE_LFN 1
#define _MAX_LFN 255
/* The _USE_LFN switches the support of long file name (LFN).
/
/ 0: Disable support of LFN. _MAX_LFN has no effect.
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
/ 2: Enable LFN with dynamic working buffer on the STACK.
/ 3: Enable LFN with dynamic working buffer on the HEAP.
/
/ To enable the LFN, Unicode handling functions (option/unicode.c) must be added
/ to the project. The working buffer occupies (_MAX_LFN + 1) * 2 bytes and
/ additional 608 bytes at exFAT enabled. _MAX_LFN can be in range from 12 to 255.
/ It should be set 255 to support full featured LFN operations.
/ When use stack for the working buffer, take care on stack overflow. When use heap
/ memory for the working buffer, memory management functions, ff_memalloc() and
/ ff_memfree(), must be added to the project. */
#define _LFN_UNICODE 0
/* This option switches character encoding on the API. (0:ANSI/OEM or 1:UTF-16)
/ To use Unicode string for the path name, enable LFN and set _LFN_UNICODE = 1.
/ This option also affects behavior of string I/O functions. */
#define _STRF_ENCODE 3
/* When _LFN_UNICODE == 1, this option selects the character encoding ON THE FILE to
/ be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf().
/
/ 0: ANSI/OEM
/ 1: UTF-16LE
/ 2: UTF-16BE
/ 3: UTF-8
/
/ This option has no effect when _LFN_UNICODE == 0. */
#define _FS_RPATH 2
/* This option configures support of relative path.
/
/ 0: Disable relative path and remove related functions.
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
/ 2: f_getcwd() function is available in addition to 1.
*/
/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/
#define _VOLUMES 1
/* Number of volumes (logical drives) to be used. */
#define _STR_VOLUME_ID 0
#define _VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
/* _STR_VOLUME_ID switches string support of volume ID.
/ When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
/ number in the path name. _VOLUME_STRS defines the drive ID strings for each
/ logical drives. Number of items must be equal to _VOLUMES. Valid characters for
/ the drive ID strings are: A-Z and 0-9. */
#define _MULTI_PARTITION 0
/* This option switches support of multi-partition on a physical drive.
/ By default (0), each logical drive number is bound to the same physical drive
/ number and only an FAT volume found on the physical drive will be mounted.
/ When multi-partition is enabled (1), each logical drive number can be bound to
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
/ funciton will be available. */
#define _MIN_SS 512
#define _MAX_SS 512
/* These options configure the range of sector size to be supported. (512, 1024,
/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and
/ harddisk. But a larger value may be required for on-board flash memory and some
/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured
/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the
/ disk_ioctl() function. */
#define _USE_TRIM 0
/* This option switches support of ATA-TRIM. (0:Disable or 1:Enable)
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
/ disk_ioctl() function. */
#define _FS_NOFSINFO 0
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/ option, and f_getfree() function at first time after volume mount will force
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/ bit0=0: Use free cluster count in the FSINFO if available.
/ bit0=1: Do not trust free cluster count in the FSINFO.
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
*/
/*---------------------------------------------------------------------------/
/ System Configurations
/---------------------------------------------------------------------------*/
#define _FS_TINY 0
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
/ At the tiny configuration, size of file object (FIL) is reduced _MAX_SS bytes.
/ Instead of private sector buffer eliminated from the file object, common sector
/ buffer in the file system object (FATFS) is used for the file data transfer. */
#define _FS_EXFAT 1
/* This option switches support of exFAT file system. (0:Disable or 1:Enable)
/ When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1)
/ Note that enabling exFAT discards C89 compatibility. */
#define _FS_NORTC 0
#define _NORTC_MON 1
#define _NORTC_MDAY 1
#define _NORTC_YEAR 2016
/* The option _FS_NORTC switches timestamp functiton. If the system does not have
/ any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable
/ the timestamp function. All objects modified by FatFs will have a fixed timestamp
/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR in local time.
/ To enable timestamp function (_FS_NORTC = 0), get_fattime() function need to be
/ added to the project to get current time form real-time clock. _NORTC_MON,
/ _NORTC_MDAY and _NORTC_YEAR have no effect.
/ These options have no effect at read-only configuration (_FS_READONLY = 1). */
#define _FS_LOCK 0
/* The option _FS_LOCK switches file lock function to control duplicated file open
/ and illegal operation to open objects. This option must be 0 when _FS_READONLY
/ is 1.
/
/ 0: Disable file lock function. To avoid volume corruption, application program
/ should avoid illegal open, remove and rename to the open objects.
/ >0: Enable file lock function. The value defines how many files/sub-directories
/ can be opened simultaneously under file lock control. Note that the file
/ lock control is independent of re-entrancy. */
#define _FS_REENTRANT 0
#define _FS_TIMEOUT 1000
#define _SYNC_t HANDLE
/* The option _FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
/ module itself. Note that regardless of this option, file access to different
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
/ to the same volume is under control of this function.
/
/ 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect.
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
/ function, must be added to the project. Samples are available in
/ option/syscall.c.
/
/ The _FS_TIMEOUT defines timeout period in unit of time tick.
/ The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
/ SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be
/ included somewhere in the scope of ff.h. */
/* #include <windows.h> // O/S definitions */
/*--- End of configuration options ---*/

BIN
font Normal file

Binary file not shown.

1386
gcr.cpp Normal file

File diff suppressed because it is too large Load Diff

162
gcr.h Normal file
View File

@ -0,0 +1,162 @@
/*
gcr.h - Group Code Recording helper functions
(C) 2001-05 Markus Brenner <markus(at)brenner(dot)de>
and Pete Rittwage <peter(at)rittwage(dot)com>
based on code by Andreas Boose
V 0.33 improved sector extraction, added find_track_cycle() function
V 0.34 added MAX_SYNC_OFFSET constant, approximated to 800 GCR bytes
V 0.35 modified find_track_cycle() interface
V 0.36 added bad GCR code detection
V 0.36a added find_sector_gap(), find_sector0(), extract_GCR_track()
V 0.36d Untold number of additions and consequent bugfixes. (pjr)
*/
#ifndef _GCR_
#define _GCR_
#include "integer.h" /* Basic integer types */
//#define BYTE unsigned char
//#define DWORD unsigned int
#define MAX_TRACKS_1541 42
#define MAX_TRACKS_1571 (MAX_TRACKS_1541 * 2)
#define MAX_HALFTRACKS_1541 (MAX_TRACKS_1541 * 2)
#define MAX_HALFTRACKS_1571 (MAX_TRACKS_1571 * 2)
/* D64 constants */
#define BLOCKSONDISK 683
#define BLOCKSEXTRA 85
#define MAXBLOCKSONDISK (BLOCKSONDISK+BLOCKSEXTRA)
#define MAX_TRACK_D64 40
/* G64 constants (only needed for current VICE support */
#define G64_TRACK_MAXLEN 7928
#define G64_TRACK_LENGTH (G64_TRACK_MAXLEN+2)
/* NIB format constants */
#define NIB_TRACK_LENGTH 0x2000
#define NIB_HEADER_SIZE 0xFF
/*
number of GCR bytes until NO SYNC error
timer counts down from $d000 to $8000 (20480 cycles)
until timeout when waiting for a SYNC signal
This is approx. 20.48 ms, which is approx 1/10th disk revolution
8000 GCR bytes / 10 = 800 bytes
*/
//#define MAX_SYNC_OFFSET 800
/* this was too small for Lode Runner original (805), so increase to 820 */
//#define MAX_SYNC_OFFSET 820
#define MAX_SYNC_OFFSET 0x1500
#define SIGNIFICANT_GAPLEN_DIFF 0x20
#define GCR_BLOCK_HEADER_LEN 24
#define GCR_BLOCK_DATA_LEN 337
#define GCR_BLOCK_LEN (GCR_BLOCK_HEADER_LEN + GCR_BLOCK_DATA_LEN)
/* To calculate the bytes per rotation:
4,000,000 * 60
b/minute = ------------------------------------------------ = x bytes/minute
speed_zone_divisor * 8bits
4,000,000 is the base clock frequency divided by 4.
8 is the number of bits per byte.
60 gets us to a minute of data, which we can then divide by RPM to
get our numbers.
speed zone divisors are 13, 14, 15, 16 for densities 3, 2, 1, 0 respectively
*/
#define DENSITY3 2307692.308 // bytes per minute
#define DENSITY2 2142857.143
#define DENSITY1 2000000.000
#define DENSITY0 1875000.000
/* Some disks have much less data than we normally expect to be able to write at a given density.
It's like short tracks, but it's a mastering issue not a protection.
This keeps us from getting errors in the track cycle detection */
#define CAP_MIN_ALLOWANCE 150
/* minimum amount of good sequential GCR for formatted track */
#define GCR_MIN_FORMATTED 64 // chessmaster track 29 is shortest so far
/* Disk Controller error codes */
#define SECTOR_OK 0x01
#define HEADER_NOT_FOUND 0x02
#define SYNC_NOT_FOUND 0x03
#define DATA_NOT_FOUND 0x04
#define BAD_DATA_CHECKSUM 0x05
#define BAD_GCR_CODE 0x06
#define VERIFY_ERROR 0x07
#define WRITE_PROTECTED 0x08
#define BAD_HEADER_CHECKSUM 0x09
#define ID_MISMATCH 0x0b
#define DISK_NOT_INSERTED 0x0f
#define BM_MATCH 0x10
#define BM_NO_CYCLE 0x20
#define BM_NO_SYNC 0x40
#define BM_FF_TRACK 0x80
#define ALIGN_NONE 0
#define ALIGN_GAP 1
#define ALIGN_SEC0 2
#define ALIGN_LONGSYNC 3
#define ALIGN_WEAK 4
#define ALIGN_VMAX 5
#define ALIGN_AUTOGAP 6
#define GCR_MASK_BAD_FIRST 0
#define GCR_MASK_BAD_LAST 1
/* global variables */
extern char sector_map_1541[];
extern BYTE speed_map_1541[];
extern int capacity[];
extern int capacity_min[];
extern int capacity_max[];\
extern int gap_match_length;
/* prototypes */
int find_sync(BYTE ** gcr_pptr, BYTE * gcr_end);
void convert_4bytes_to_GCR(BYTE * buffer, BYTE * ptr);
int convert_4bytes_from_GCR(BYTE * gcr, BYTE * plain);
int extract_id(BYTE * gcr_track, BYTE * id);
int extract_cosmetic_id(BYTE * gcr_track, BYTE * id);
size_t find_track_cycle(BYTE ** cycle_start, BYTE ** cycle_stop, int cap_min,
int cap_max);
size_t find_nondos_track_cycle(BYTE ** cycle_start, BYTE ** cycle_stop,
int cap_min, int cap_max);
BYTE convert_GCR_sector(BYTE * gcr_start, BYTE * gcr_end,
BYTE * d64_sector, int track, int sector, BYTE * id);
void convert_sector_to_GCR(BYTE * buffer, BYTE * ptr,
int track, int sector, BYTE * diskID, int error);
BYTE * find_sector_gap(BYTE * work_buffer, int tracklen, size_t * p_sectorlen);
BYTE * find_sector0(BYTE * work_buffer, int tracklen, size_t * p_sectorlen);
int extract_GCR_track(BYTE * destination, BYTE * source, int * align,
int force_align, size_t cap_min, size_t cap_max);
int replace_bytes(BYTE * buffer, int length, BYTE srcbyte, BYTE dstbyte);
int check_bad_gcr(BYTE * gcrdata, int length, int fix);
int check_sync_flags(BYTE * gcrdata, int density, int length);
void bitshift(BYTE * gcrdata, int length, int bits);
int check_errors(BYTE * gcrdata, int length, int track, BYTE * id,
char * errorstring);
int check_empty(BYTE * gcrdata, int length, int track, BYTE * id,
char * errorstring);
int compare_tracks(BYTE * track1, BYTE * track2, int length1, int length2,
int same_disk, char * outputstring);
int compare_sectors(BYTE * track1, BYTE * track2, int length1, int length2,
BYTE * id1, BYTE * id2, int track, char * outputstring);
int strip_runs(BYTE * buffer, int length, int minrun, BYTE target);
int reduce_runs(BYTE * buffer, int length, int length_max, int minrun,
BYTE target);
int is_bad_gcr(BYTE * gcrdata, size_t length, size_t pos);
int check_formatted(BYTE * gcrdata);
int check_valid_data(BYTE * data, int length);
#endif

126
iec_bus.cpp Normal file
View File

@ -0,0 +1,126 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#include "iec_bus.h"
u32 IEC_Bus::PIGPIO_MASK_IN_ATN = 1 << PIGPIO_ATN;
u32 IEC_Bus::PIGPIO_MASK_IN_DATA = 1 << PIGPIO_DATA;
u32 IEC_Bus::PIGPIO_MASK_IN_CLOCK = 1 << PIGPIO_CLOCK;
u32 IEC_Bus::PIGPIO_MASK_IN_SRQ = 1 << PIGPIO_SRQ;
u32 IEC_Bus::PIGPIO_MASK_IN_RESET = 1 << PIGPIO_RESET;
bool IEC_Bus::PI_Atn = false;
bool IEC_Bus::PI_Data = false;
bool IEC_Bus::PI_Clock = false;
bool IEC_Bus::PI_Reset = false;
bool IEC_Bus::VIA_Atna = false;
bool IEC_Bus::VIA_Data = false;
bool IEC_Bus::VIA_Clock = false;
bool IEC_Bus::DataSetToOut = false;
bool IEC_Bus::AtnaDataSetToOut = false;
bool IEC_Bus::ClockSetToOut = false;
bool IEC_Bus::OutputLED = false;
bool IEC_Bus::OutputSound = false;
bool IEC_Bus::Resetting = false;
bool IEC_Bus::splitIECLines = false;
bool IEC_Bus::invertIECInputs = false;
u32 IEC_Bus::myOutsGPFSEL1 = 0;
u32 IEC_Bus::myOutsGPFSEL0 = 0;
bool IEC_Bus::InputButton[5];
bool IEC_Bus::InputButtonPrev[5];
u32 IEC_Bus::validInputCount[5];
u32 IEC_Bus::inputRepeatThreshold[5];
u32 IEC_Bus::inputRepeat[5];
u32 IEC_Bus::inputRepeatPrev[5];
m6522* IEC_Bus::VIA = 0;
void IEC_Bus::Read(void)
{
IOPort* portB = 0;
unsigned gplev0 = read32(ARM_GPIO_GPLEV0);
int index;
int buttonCount = sizeof(ButtonPinFlags) / sizeof(unsigned);
for (index = 0; index < buttonCount; ++index)
{
UpdateButton(index, gplev0);
}
if (VIA) portB = VIA->GetPortB();
bool ATNIn = (gplev0 & PIGPIO_MASK_IN_ATN) == (invertIECInputs ? PIGPIO_MASK_IN_ATN : 0);
if (PI_Atn != ATNIn)
{
PI_Atn = ATNIn;
if (VIA)
{
if ((portB->GetDirection() & 0x10) != 0)
{
// Emulate the XOR gate UD3
// We only need to do this when fully emulating, iec commands do this internally
AtnaDataSetToOut = (VIA_Atna != PI_Atn);
}
portB->SetInput(VIAPORTPINS_ATNIN, ATNIn); //is inverted and then connected to pb7 and ca1
VIA->InputCA1(ATNIn);
}
}
if (portB && (portB->GetDirection() & 0x10) == 0)
AtnaDataSetToOut = false; // If the ATNA PB4 gets set to an input then we can't be pulling data low. (Maniac Mansion does this)
if (!AtnaDataSetToOut && !DataSetToOut) // only sense if we have not brought the line low (because we can't as we have the pin set to output but we can simulate in software)
{
bool DATAIn = (gplev0 & PIGPIO_MASK_IN_DATA) == (invertIECInputs ? PIGPIO_MASK_IN_DATA : 0);
if (PI_Data != DATAIn)
{
PI_Data = DATAIn;
if (VIA) portB->SetInput(VIAPORTPINS_DATAIN, DATAIn); // VIA DATAin pb0 output from inverted DIN 5 DATA
}
}
else
{
PI_Data = true;
if (VIA) portB->SetInput(VIAPORTPINS_DATAIN, true); // simulate the read in software
}
if (!ClockSetToOut) // only sense if we have not brought the line low (because we can't as we have the pin set to output but we can simulate in software)
{
bool CLOCKIn = (gplev0 & PIGPIO_MASK_IN_CLOCK) == (invertIECInputs ? PIGPIO_MASK_IN_CLOCK : 0);
if (PI_Clock != CLOCKIn)
{
PI_Clock = CLOCKIn;
if (VIA) portB->SetInput(VIAPORTPINS_CLOCKIN, CLOCKIn); // VIA CLKin pb2 output from inverted DIN 4 CLK
}
}
else
{
PI_Clock = true;
if (VIA) portB->SetInput(VIAPORTPINS_CLOCKIN, true); // simulate the read in software
}
Resetting = (gplev0 & PIGPIO_MASK_IN_RESET) == (invertIECInputs ? PIGPIO_MASK_IN_RESET : 0);
}

602
iec_bus.h Normal file
View File

@ -0,0 +1,602 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#ifndef IEC_BUS_H
#define IEC_BUS_H
#include "debug.h"
#include "m6522.h"
#include "rpi-gpio.h"
#include "rpiHardware.h"
#define INPUT_BUTTON_DEBOUNCE_THRESHOLD 20000
#define INPUT_BUTTON_REPEAT_THRESHOLD 460000
// DIN ATN is inverted and then connected to pb7 and ca1
// - also input to xor with ATNAout pb4
// - output of xor is inverted and connected to DIN pin 5 DATAout (OC so can only pull low)
// VIA ATNin pb7 input to xor
// VIA ATNin ca1 output from inverted DIN 3 ATN
// DIN DATA is inverted and then connected to pb1
// VIA DATAin pb0 output from inverted DIN 5 DATA
// VIA DATAout pb1 inverted and then connected to DIN DATA
// DIN CLK is inverted and then connected to pb2
// VIA CLKin pb2 output from inverted DIN 4 CLK
// VIA CLKout pb3 inverted and then connected to DIN CLK
// $1800
// PB 0 data in
// PB 1 data out
// PB 2 clock in
// PB 3 clock out
// PB 4 ATNA
// PB 5,6 device address
// PB 7,CA1 ATN IN
// If ATN and ATNA are out of sync
// - VIA's DATA IN will automatically set high and hence the DATA line will be pulled low (activated)
// If ATN and ATNA are in sync
// - the output from the XOR gate will be low and the output of its inverter will go high
// - when this occurs the DATA line must be still able to be pulled low via the PC or VIA's inverted PB1 (DATA OUT)
//
// Therefore in the same vein if PB7 is set to output it could cause the input of the XOR to be pulled low
//
enum PIGPIO
{
// Original Non-split lines
PIGPIO_ATN = 2, // 3
PIGPIO_DATA = 18, // 12
PIGPIO_CLOCK = 17, // 11
PIGPIO_SRQ = 19, // 35
PIGPIO_RESET = 3, // 5
// Pinout for those that want to split the lines (and the common ones like buttons, sound and LED)
// 0 IDSC //28
// 1 IDSD //27
// 2 I2C_SDA //3
// 3 I2C_CLK //5
PIGPIO_IN_BUTTON4 = 4, // 07 Common
PIGPIO_IN_BUTTON5 = 5, // 29 Common
PIGPIO_OUT_RESET = 6, // 31
// 7 SPI0_CS1 //26
// 8 SPI0_CS0 //24
// 9 SPI0_MISO //21
// 10 SPI0_MOSI //19
// 11 SPI0_CLK //23
PIGPIO_OUT_ATN = 12, // 32
PIGPIO_OUT_SOUND = 13, // 33 Common
// 14 TX //8
// 15 RX //10
PIGPIO_OUT_LED = 16, // 36 Common
PIGPIO_OUT_CLOCK = 17, // 11
PIGPIO_OUT_DATA = 18, // 12
PIGPIO_OUT_SRQ = 19, // 35
PIGPIO_IN_RESET = 20, // 38
PIGPIO_IN_SRQ = 21, // 40
PIGPIO_IN_BUTTON2 = 22, // 15 Common
PIGPIO_IN_BUTTON3 = 23, // 16 Common
PIGPIO_IN_ATN = 24, // 18
PIGPIO_IN_DATA = 25, // 22
PIGPIO_IN_CLOCK = 26, // 37
PIGPIO_IN_BUTTON1 = 27 // 13 Common
};
enum PIGPIOMasks
{
// Non-split lines
//PIGPIO_MASK_IN_ATN = 1 << PIGPIO_ATN,
//PIGPIO_MASK_IN_DATA = 1 << PIGPIO_DATA,
//PIGPIO_MASK_IN_CLOCK = 1 << PIGPIO_CLOCK,
//PIGPIO_MASK_IN_SRQ = 1 << PIGPIO_SRQ,
//PIGPIO_MASK_IN_RESET = 1 << PIGPIO_RESET,
// Split lines
//PIGPIO_MASK_IN_ATN = 1 << PIGPIO_IN_ATN,
//PIGPIO_MASK_IN_DATA = 1 << PIGPIO_IN_DATA,
//PIGPIO_MASK_IN_CLOCK = 1 << PIGPIO_IN_CLOCK,
//PIGPIO_MASK_IN_SRQ = 1 << PIGPIO_IN_SRQ,
//PIGPIO_MASK_IN_RESET = 1 << PIGPIO_IN_RESET,
PIGPIO_MASK_IN_BUTTON1 = 1 << PIGPIO_IN_BUTTON1,
PIGPIO_MASK_IN_BUTTON2 = 1 << PIGPIO_IN_BUTTON2,
PIGPIO_MASK_IN_BUTTON3 = 1 << PIGPIO_IN_BUTTON3,
PIGPIO_MASK_IN_BUTTON4 = 1 << PIGPIO_IN_BUTTON4,
PIGPIO_MASK_IN_BUTTON5 = 1 << PIGPIO_IN_BUTTON5,
};
static const unsigned ButtonPinFlags[5] = { PIGPIO_MASK_IN_BUTTON1, PIGPIO_MASK_IN_BUTTON2, PIGPIO_MASK_IN_BUTTON3, PIGPIO_MASK_IN_BUTTON4, PIGPIO_MASK_IN_BUTTON5 };
///////////////////////////////////////////////////////////////////////////////////////////////
// Original Non-split lines
///////////////////////////////////////////////////////////////////////////////////////////////
// I kind of stuffed up selecting what pins should be what.
// Originally I wanted the hardware interface to be as simple as possible and was focused on all the connections down one end of the Pi header.
// Also, originally I was only worried about ATN, DATA, CLOCK and RESET. Buttons were optional. With DATA and CLOCK only needing to go output.
// With hindsight (now wanting to support NIBTools ATN should have been put in the same pin group as DATA and CLOCK);
// This now requires 2 GPIO writes as PIN2 is in SEL0 and pins 17 and 18 are in SEL1.
//GPFSEL0
// 9 8 7 6 5 4 3 2 1 0
// 000 000 000 000 000 000 000 001 000 000
// To support NIBTools ATN can be made an output as well
static const u32 PI_OUTPUT_MASK_GPFSEL0 = ~((1 << (PIGPIO_ATN * 3)));
//GPFSEL1 RX TX
// 19 18 17 16 15 14 13 12 11 10
// 000 001 001 000 000 000 000 000 000 000 (bits 21 and 24 for GPIO 17 and 18 as outputs)
//static const u32 PI_OUTPUT_MASK_GPFSEL1 = ~((1 << 21) | (1 << 24));
// 000 001 001 001 000 000 001 000 000 000 (bits 21 and 24 for GPIO 17 and 18 as outputs)
//static const u32 PI_OUTPUT_MASK_GPFSEL1 = ~((1 << ((PIGPIO_OUT_SOUND - 10) * 3)) | (1 << ((PIGPIO_OUT_LED - 10) * 3)) | (1 << ((PIGPIO_CLOCK - 10) * 3)) | (1 << ((PIGPIO_DATA - 10) * 3)));
static const u32 PI_OUTPUT_MASK_GPFSEL1 = ~((1 << ((PIGPIO_CLOCK - 10) * 3)) | (1 << ((PIGPIO_DATA - 10) * 3)));
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// Pinout for those that want to split the lines (and the common ones like buttons, sound and LED)
///////////////////////////////////////////////////////////////////////////////////////////////
// OUT (need 7)
// CLK
// DATA
// SRQ
// ATN
// RST
// LED
// SND
// IN (need 10)
// CLK
// DATA
// ATN
// RST
// SRQ
// BTN1
// BTN2
// BTN3
// BTN4
// BTN5
//GPFSEL0
// S S S
// P P P I I
// I I I 2 2
// 0 0 0 C C
// M C C S S I I
// I E E C D D D
// S 0 1 W R R L A S S
// O RST B5 B4 1 1 D C
// 9 8 7 6 5 4 3 2 1 0
//GPFSEL1
// RX TX S S
// RX TX P P
// RX TX I I
// RX TX 0 0
// RX TX M
// RX TX C O
// W W W W RX TX W W L S
// SRQ DTA CLK LED RX TX SND ATN K I
// 19 18 17 16 15 14 13 12 11 10
//GPFSEL2
// X X
// X X
// X X
// X X
// X X
// X X
// X X R R R R R R R R
// X X B1 CLK DTA ATN B3 B2 SRQ RST
// 29 28 27 26 25 24 23 22 21 20
// 000 000 000 000 000 000 000 000 000 000
///////////////////////////////////////////////////////////////////////////////////////////////
enum VIAPortPins
{
VIAPORTPINS_DATAIN = 0x01, //pb0
VIAPORTPINS_DATAOUT = 0x02, //pb1
VIAPORTPINS_CLOCKIN = 0x04, //pb2
VIAPORTPINS_CLOCKOUT = 0x08,//pb3
VIAPORTPINS_ATNAOUT = 0x10, //pb4
VIAPORTPINS_DEVSEL0 = 0x20, //pb5
VIAPORTPINS_DEVSEL1 = 0x40, //pb5
VIAPORTPINS_ATNIN = 0x80 //bp7
};
typedef bool(*CheckStatus)();
class IEC_Bus
{
public:
static inline void Initialise(void)
{
volatile int index; // Force a real delay in the loop below.
// Clear all outputs to 0
write32(ARM_GPIO_GPCLR0, 0xFFFFFFFF);
if (!splitIECLines)
{
// This means that when any pin is turn to output it will output a 0 and pull lines low (ie an activation state on the IEC bus)
// Note: on the IEC bus you never output a 1 you simply tri state and it will be pulled up to a 1 (ie inactive state on the IEC bus) if no one else is pulling it low.
myOutsGPFSEL0 = read32(ARM_GPIO_GPFSEL0);
myOutsGPFSEL1 = read32(ARM_GPIO_GPFSEL1);
myOutsGPFSEL1 |= (1 << ((PIGPIO_OUT_LED - 10) * 3));
myOutsGPFSEL1 |= (1 << ((PIGPIO_OUT_SOUND - 10) * 3));
}
else
{
RPI_SetGpioPinFunction((rpi_gpio_pin_t)PIGPIO_IN_BUTTON4, FS_INPUT);
RPI_SetGpioPinFunction((rpi_gpio_pin_t)PIGPIO_IN_BUTTON5, FS_INPUT);
RPI_SetGpioPinFunction((rpi_gpio_pin_t)PIGPIO_IN_RESET, FS_INPUT);
RPI_SetGpioPinFunction((rpi_gpio_pin_t)PIGPIO_IN_SRQ, FS_INPUT);
RPI_SetGpioPinFunction((rpi_gpio_pin_t)PIGPIO_IN_BUTTON2, FS_INPUT);
RPI_SetGpioPinFunction((rpi_gpio_pin_t)PIGPIO_IN_BUTTON3, FS_INPUT);
RPI_SetGpioPinFunction((rpi_gpio_pin_t)PIGPIO_IN_ATN, FS_INPUT);
RPI_SetGpioPinFunction((rpi_gpio_pin_t)PIGPIO_IN_DATA, FS_INPUT);
RPI_SetGpioPinFunction((rpi_gpio_pin_t)PIGPIO_IN_CLOCK, FS_INPUT);
RPI_SetGpioPinFunction((rpi_gpio_pin_t)PIGPIO_IN_BUTTON1, FS_INPUT);
RPI_SetGpioPinFunction((rpi_gpio_pin_t)PIGPIO_OUT_RESET, FS_OUTPUT);
RPI_SetGpioPinFunction((rpi_gpio_pin_t)PIGPIO_OUT_ATN, FS_OUTPUT);
RPI_SetGpioPinFunction((rpi_gpio_pin_t)PIGPIO_OUT_SOUND, FS_OUTPUT);
RPI_SetGpioPinFunction((rpi_gpio_pin_t)PIGPIO_OUT_LED, FS_OUTPUT);
RPI_SetGpioPinFunction((rpi_gpio_pin_t)PIGPIO_OUT_CLOCK, FS_OUTPUT);
RPI_SetGpioPinFunction((rpi_gpio_pin_t)PIGPIO_OUT_DATA, FS_OUTPUT);
RPI_SetGpioPinFunction((rpi_gpio_pin_t)PIGPIO_OUT_SRQ, FS_OUTPUT);
}
// Set up audio.
write32(CM_PWMDIV, CM_PASSWORD + 0x2000);
write32(CM_PWMCTL, CM_PASSWORD + CM_ENAB + CM_SRC_OSCILLATOR); // Use Default 100MHz Clock
// Set PWM
write32(PWM_RNG1, 0x1B4); // 8bit 44100Hz Mono
write32(PWM_RNG2, 0x1B4);
write32(PWM_CTL, PWM_USEF2 + PWM_PWEN2 + PWM_USEF1 + PWM_PWEN1 + PWM_CLRF1);
int buttonCount = sizeof(ButtonPinFlags) / sizeof(unsigned);
for (index = 0; index < buttonCount; ++index)
{
InputButton[index] = false;
InputButtonPrev[index] = false;
validInputCount[index] = 0;
inputRepeatThreshold[index] = INPUT_BUTTON_REPEAT_THRESHOLD;
inputRepeat[index] = 0;
inputRepeatPrev[index] = 0;
}
// Enable the internal pullups for the input button pins using the method described in BCM2835-ARM-Peripherals manual.
RPI_GpioBase->GPPUD = 2;
for (index = 0; index < 150; ++index)
{
}
RPI_GpioBase->GPPUDCLK0 = PIGPIO_MASK_IN_BUTTON1 | PIGPIO_MASK_IN_BUTTON2 | PIGPIO_MASK_IN_BUTTON3 | PIGPIO_MASK_IN_BUTTON4 | PIGPIO_MASK_IN_BUTTON5;
for (index = 0; index < 150; ++index)
{
}
RPI_GpioBase->GPPUD = 0;
RPI_GpioBase->GPPUDCLK0 = 0;
}
static inline void UpdateButton(int index, unsigned gplev0)
{
bool inputcurrent = (gplev0 & ButtonPinFlags[index]) == 0;
InputButtonPrev[index] = InputButton[index];
inputRepeatPrev[index] = inputRepeat[index];
if (inputcurrent)
{
validInputCount[index]++;
if (validInputCount[index] == INPUT_BUTTON_DEBOUNCE_THRESHOLD)
{
InputButton[index] = true;
inputRepeatThreshold[index] = INPUT_BUTTON_DEBOUNCE_THRESHOLD + INPUT_BUTTON_REPEAT_THRESHOLD;
inputRepeat[index]++;
}
if (validInputCount[index] == inputRepeatThreshold[index])
{
inputRepeat[index]++;
inputRepeatThreshold[index] += INPUT_BUTTON_REPEAT_THRESHOLD / inputRepeat[index];
}
}
else
{
InputButton[index] = false;
validInputCount[index] = 0;
inputRepeatThreshold[index] = INPUT_BUTTON_REPEAT_THRESHOLD;
inputRepeat[index] = 0;
inputRepeatPrev[index] = 0;
}
}
static void Read(void);
static void WaitUntilReset(void)
{
unsigned gplev0;
do
{
gplev0 = read32(ARM_GPIO_GPLEV0);
Resetting = (gplev0 & PIGPIO_MASK_IN_RESET) == (invertIECInputs ? PIGPIO_MASK_IN_RESET : 0);
if (Resetting)
IEC_Bus::WaitMicroSeconds(100);
}
while (Resetting);
}
// Out going
static void PortB_OnPortOut(void* pUserData, unsigned char status)
{
bool oldDataSetToOut = DataSetToOut;
bool oldClockSetToOut = ClockSetToOut;
// These are the values the VIA is trying to set the outputs to
VIA_Atna = (status & (unsigned char)VIAPORTPINS_ATNAOUT) != 0;
VIA_Data = (status & (unsigned char)VIAPORTPINS_DATAOUT) != 0; // VIA DATAout PB1 inverted and then connected to DIN DATA
VIA_Clock = (status & (unsigned char)VIAPORTPINS_CLOCKOUT) != 0; // VIA CLKout PB3 inverted and then connected to DIN CLK
// Emulate the XOR gate UD3
AtnaDataSetToOut = (VIA_Atna != PI_Atn);
if (AtnaDataSetToOut)
{
// if the output of the XOR gate is high (ie VIA_Atna != PI_Atn) then this is inverted and pulls DATA low (activating it)
PI_Data = true;
if (VIA) VIA->GetPortB()->SetInput(VIAPORTPINS_DATAIN, true); // simulate the read in software
}
if (VIA)
{
// If the VIA's data and clock outputs ever get set to inputs the real hardware reads these lines as asserted.
bool PB1SetToInput = (VIA->GetPortB()->GetDirection() & 2) == 0;
bool PB3SetToInput = (VIA->GetPortB()->GetDirection() & 8) == 0;
if (PB1SetToInput) VIA_Data = true;
if (PB3SetToInput) VIA_Clock = true;
}
ClockSetToOut = VIA_Clock;
DataSetToOut = VIA_Data;
if (!oldDataSetToOut && DataSetToOut)
{
PI_Data = true;
if (VIA) VIA->GetPortB()->SetInput(VIAPORTPINS_DATAOUT, true); // simulate the read in software
}
if (!oldClockSetToOut && ClockSetToOut)
{
PI_Clock = true;
if (VIA) VIA->GetPortB()->SetInput(VIAPORTPINS_CLOCKIN, true); // simulate the read in software
}
}
static inline void RefreshOuts(void)
{
unsigned set = 0;
unsigned clear = 0;
if (!splitIECLines)
{
unsigned outputs = 0;
if (AtnaDataSetToOut || DataSetToOut) outputs |= (FS_OUTPUT << ((PIGPIO_DATA - 10) * 3));
if (ClockSetToOut) outputs |= (FS_OUTPUT << ((PIGPIO_CLOCK - 10) * 3));
unsigned nValue = (myOutsGPFSEL1 & PI_OUTPUT_MASK_GPFSEL1) | outputs;
write32(ARM_GPIO_GPFSEL1, nValue);
}
else
{
if (AtnaDataSetToOut || DataSetToOut) set |= 1 << PIGPIO_OUT_DATA;
else clear |= 1 << PIGPIO_OUT_DATA;
if (ClockSetToOut) set |= 1 << PIGPIO_OUT_CLOCK;
else clear |= 1 << PIGPIO_OUT_CLOCK;
}
if (OutputLED) set |= 1 << PIGPIO_OUT_LED;
else clear |= 1 << PIGPIO_OUT_LED;
if (OutputSound) set |= 1 << PIGPIO_OUT_SOUND;
else clear |= 1 << PIGPIO_OUT_SOUND;
write32(ARM_GPIO_GPSET0, set);
write32(ARM_GPIO_GPCLR0, clear);
}
static void WaitMicroSeconds(u32 amount)
{
u32 count;
for (count = 0; count < amount; ++count)
{
unsigned before;
unsigned after;
// We try to update every micro second and use as a rough timer to count micro seconds
before = read32(ARM_SYSTIMER_CLO);
do
{
after = read32(ARM_SYSTIMER_CLO);
} while (after == before);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
// Manual methods used by IEC_Commands
static inline void AssertData()
{
if (!DataSetToOut)
{
DataSetToOut = true;
RefreshOuts();
}
}
static inline void ReleaseData()
{
if (DataSetToOut)
{
DataSetToOut = false;
RefreshOuts();
}
}
static inline void AssertClock()
{
if (!ClockSetToOut)
{
ClockSetToOut = true;
RefreshOuts();
}
}
static inline void ReleaseClock()
{
if (ClockSetToOut)
{
ClockSetToOut = false;
RefreshOuts();
}
}
static inline bool GetPI_Atn() { return PI_Atn; }
static inline bool IsAtnAsserted() { return PI_Atn; }
static inline bool IsAtnReleased() { return !PI_Atn; }
static inline bool GetPI_Data() { return PI_Data; }
static inline bool IsDataAsserted() { return PI_Data; }
static inline bool IsDataReleased() { return !PI_Data; }
static inline bool GetPI_Clock() { return PI_Clock; }
static inline bool IsClockAsserted() { return PI_Clock; }
static inline bool IsClockReleased() { return !PI_Clock; }
static inline bool GetPI_Reset() { return PI_Reset; }
static inline bool IsDataSetToOut() { return DataSetToOut; }
static inline bool IsAtnaDataSetToOut() { return AtnaDataSetToOut; }
static inline bool IsClockSetToOut() { return ClockSetToOut; }
static inline bool IsReset() { return Resetting; }
static inline void WaitWhileAtnAsserted()
{
while (IsAtnAsserted())
{
Read();
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
static inline void SetSplitIECLines(bool value)
{
splitIECLines = value;
if (splitIECLines)
{
PIGPIO_MASK_IN_ATN = 1 << PIGPIO_IN_ATN;
PIGPIO_MASK_IN_DATA = 1 << PIGPIO_IN_DATA;
PIGPIO_MASK_IN_CLOCK = 1 << PIGPIO_IN_CLOCK;
PIGPIO_MASK_IN_SRQ = 1 << PIGPIO_IN_SRQ;
PIGPIO_MASK_IN_RESET = 1 << PIGPIO_IN_RESET;
}
}
static inline void SetInvertIECInputs(bool value)
{
invertIECInputs = value;
if (value)
{
PI_Atn = ~PI_Atn;
PI_Data = ~PI_Data;
PI_Clock = ~PI_Clock;
PI_Reset = ~PI_Reset;
}
}
// CA1 input ATN
// If CA1 is ever set to output
// - CA1 will start to drive pb7
// CA2, CB1 and CB2 are not connected
// - check if pulled high or low
static m6522* VIA;
static inline void Reset(void)
{
WaitUntilReset();
// VIA $1800
// CA2, CB1 and CB2 are not connected (reads as high)
// VIA $1C00
// CB1 not connected (reads as high)
VIA_Atna = false;
VIA_Data = false;
VIA_Clock = false;
DataSetToOut = false;
ClockSetToOut = false;
PI_Atn = false;
PI_Data = false;
PI_Clock = false;
AtnaDataSetToOut = (VIA_Atna != PI_Atn);
if (AtnaDataSetToOut) PI_Data = true;
RefreshOuts();
}
static bool GetInputButtonPressed(int buttonIndex) { return InputButton[buttonIndex] && !InputButtonPrev[buttonIndex]; }
static bool GetInputButtonReleased(int buttonIndex) { return !InputButton[buttonIndex] && InputButtonPrev[buttonIndex]; }
static bool GetInputButton(int buttonIndex) { return InputButton[buttonIndex]; }
static bool GetInputButtonRepeating(int buttonIndex) { return inputRepeat[buttonIndex] != inputRepeatPrev[buttonIndex]; }
static bool OutputLED;
static bool OutputSound;
private:
static bool splitIECLines;
static bool invertIECInputs;
static u32 PIGPIO_MASK_IN_ATN;
static u32 PIGPIO_MASK_IN_DATA;
static u32 PIGPIO_MASK_IN_CLOCK;
static u32 PIGPIO_MASK_IN_SRQ;
static u32 PIGPIO_MASK_IN_RESET;
static bool PI_Atn;
static bool PI_Data;
static bool PI_Clock;
static bool PI_Reset;
static bool VIA_Atna;
static bool VIA_Data;
static bool VIA_Clock;
static bool DataSetToOut;
static bool AtnaDataSetToOut;
static bool ClockSetToOut;
static bool Resetting;
static u32 myOutsGPFSEL0;
static u32 myOutsGPFSEL1;
static bool InputButton[5];
static bool InputButtonPrev[5];
static u32 validInputCount[5];
static u32 inputRepeatThreshold[5];
static u32 inputRepeat[5];
static u32 inputRepeatPrev[5];
};
#endif

1875
iec_commands.cpp Normal file

File diff suppressed because it is too large Load Diff

168
iec_commands.h Normal file
View File

@ -0,0 +1,168 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#ifndef IEC_COMMANDS_H
#define IEC_COMMANDS_H
#include "iec_bus.h"
#include "ff.h"
#include "debug.h"
struct TimerMicroSeconds
{
TimerMicroSeconds()
{
count = 0;
timeout = 0;
}
void Start(u32 amount)
{
count = 0;
timeout = amount;
}
inline bool TimedOut() { return count >= timeout; }
bool Tick()
{
delay_us(1);
count++;
return TimedOut();
}
u32 count;
u32 timeout;
};
class IEC_Commands
{
public:
enum UpdateAction
{
NONE,
IMAGE_SELECTED,
DIR_PUSHED,
POP_DIR,
POP_TO_ROOT,
REFRESH,
DEVICEID_CHANGED,
RESET
};
IEC_Commands();
void Initialise();
void SetDeviceId(u8 id) { deviceID = id; }
u8 GetDeviceId() { return deviceID; }
void Reset(void);
void SimulateIECBegin(void);
UpdateAction SimulateIECUpdate(void);
const char* GetNameOfImageSelected() const { return selectedImageName; }
const FILINFO* GetImageSelected() const { return &filInfoSelectedImage; }
protected:
enum ATNSequence
{
ATN_SEQUENCE_IDLE,
ATN_SEQUENCE_ATN,
ATN_SEQUENCE_RECEIVE_COMMAND_CODE,
ATN_SEQUENCE_HANDLE_COMMAND_CODE,
ATN_SEQUENCE_COMPLETE
};
enum DeviceRole
{
DEVICE_ROLE_PASSIVE,
DEVICE_ROLE_LISTEN,
DEVICE_ROLE_TALK
};
struct Channel
{
FILINFO filInfo;
FIL file;
bool writing;
u32 cursor;
u32 bytesSent;
bool open;
u8 buffer[0x1000];
u8 command[0x100];
void Close();
bool WriteFull() const { return cursor >= sizeof(buffer); }
bool CanFit(u32 bytes) const { return bytes <= sizeof(buffer) - cursor; }
};
bool CheckATN(void);
bool WriteIECSerialPort(u8 data, bool eoi);
bool ReadIECSerialPort(u8& byte);
void Listen();
void Talk();
void LoadFile();
void SaveFile();
void AddDirectoryEntry(Channel& channel, const char* name, u16 blocks, int fileType);
void LoadDirectory();
void OpenFile();
void CloseFile(u8 secondary);
void CloseAllChannels();
void SendError();
bool Enter(DIR& dir, FILINFO& filInfo);
bool FindFirst(DIR& dir, const char* matchstr, FILINFO& filInfo);
void FolderCommand(void);
void CD(int partition, char* filename);
void MKDir(int partition, char* filename);
void RMDir(void);
void Copy(void);
void New(void);
void Rename(void);
void Scratch(void);
void Memory(void);
void User(void);
void ProcessCommand(void);
bool SendBuffer(Channel& channel, bool eoi);
UpdateAction updateAction;
u8 commandCode;
bool receivedCommand : 1;
bool receivedEOI : 1; // End Or Identify
bool usingVIC20 : 1; // When sending data we need to wait longer for the 64 as its VICII may be stealing its cycles. VIC20 does not have this problem and can accept data faster.
u8 deviceID;
u8 secondaryAddress;
ATNSequence atnSequence;
DeviceRole deviceRole;
TimerMicroSeconds timer;
Channel channels[16];
char selectedImageName[256];
FILINFO filInfoSelectedImage;
};
#endif

40
integer.h Normal file
View File

@ -0,0 +1,40 @@
/*-------------------------------------------*/
/* Integer type definitions for FatFs module */
/*-------------------------------------------*/
#ifndef _FF_INTEGER
#define _FF_INTEGER
#ifdef _WIN32 /* FatFs development platform */
#include <windows.h>
#include <tchar.h>
typedef unsigned __int64 QWORD;
#else /* Embedded platform */
/* These types MUST be 16-bit or 32-bit */
typedef int INT;
typedef unsigned int UINT;
/* This type MUST be 8-bit */
typedef unsigned char BYTE;
/* These types MUST be 16 bit */
typedef short SHORT;
typedef unsigned short WORD;
typedef unsigned short WCHAR;
/* These types MUST be 32-bit */
typedef long LONG;
typedef unsigned long DWORD;
/* This type MUST be 64-bit (Remove this for C89 compatibility) */
typedef unsigned long long QWORD;
#endif
//#include <cstdint>
#include <stdint.h>
#endif

159
interrupt.c Normal file
View File

@ -0,0 +1,159 @@
#include "interrupt.h"
#include "rpiHardware.h"
#include "bcm2835int.h"
#define ARM_IC_IRQ_PENDING(irq) ( (irq) < ARM_IRQ2_BASE \
? ARM_IC_IRQ_PENDING_1 \
: ((irq) < ARM_IRQBASIC_BASE \
? ARM_IC_IRQ_PENDING_2 \
: ARM_IC_IRQ_BASIC_PENDING))
#define ARM_IC_IRQS_ENABLE(irq) ( (irq) < ARM_IRQ2_BASE \
? ARM_IC_ENABLE_IRQS_1 \
: ((irq) < ARM_IRQBASIC_BASE \
? ARM_IC_ENABLE_IRQS_2 \
: ARM_IC_ENABLE_BASIC_IRQS))
#define ARM_IC_IRQS_DISABLE(irq) ( (irq) < ARM_IRQ2_BASE \
? ARM_IC_DISABLE_IRQS_1 \
: ((irq) < ARM_IRQBASIC_BASE \
? ARM_IC_DISABLE_IRQS_2 \
: ARM_IC_DISABLE_BASIC_IRQS))
#define ARM_IRQ_MASK(irq) (1 << ((irq) & (ARM_IRQS_PER_REG-1)))
static IRQHandler* IRQHandlers[IRQ_LINES] = { 0 };
static void* Params[IRQ_LINES] = { 0 };
void InterruptSystemInitialize()
{
InstructionSyncBarrier();
DataMemBarrier();
write32(ARM_IC_FIQ_CONTROL, 0);
write32(ARM_IC_DISABLE_IRQS_1, (u32)-1);
write32(ARM_IC_DISABLE_IRQS_2, (u32)-1);
write32(ARM_IC_DISABLE_BASIC_IRQS, (u32)-1);
// Ack pending IRQs
write32(ARM_IC_IRQ_BASIC_PENDING, read32(ARM_IC_IRQ_BASIC_PENDING));
write32(ARM_IC_IRQ_PENDING_1, read32(ARM_IC_IRQ_PENDING_1));
write32(ARM_IC_IRQ_PENDING_2, read32(ARM_IC_IRQ_PENDING_2));
DataMemBarrier();
EnableInterrupts();
}
void InterruptSystemConnectIRQ(unsigned IRQIndex, IRQHandler* handler, void* param)
{
IRQHandlers[IRQIndex] = handler;
Params[IRQIndex] = param;
InterruptSystemEnableIRQ(IRQIndex);
}
void InterruptSystemDisconnectIRQ(unsigned IRQIndex)
{
InterruptSystemDisableIRQ(IRQIndex);
IRQHandlers[IRQIndex] = 0;
Params[IRQIndex] = 0;
}
void InterruptSystemEnableIRQ(unsigned IRQIndex)
{
//DEBUG_LOG("InterruptSystemEnableIRQ %d\r\n", IRQIndex);
DataMemBarrier();
write32(ARM_IC_IRQS_ENABLE(IRQIndex), ARM_IRQ_MASK(IRQIndex));
DataMemBarrier();
}
void InterruptSystemDisableIRQ(unsigned IRQIndex)
{
DataMemBarrier();
write32 (ARM_IC_IRQS_DISABLE(IRQIndex), ARM_IRQ_MASK(IRQIndex));
DataMemBarrier();
}
void InterruptHandler(void)
{
// DEBUG_LOG("InterruptHandler\r\n");
DataMemBarrier();
//(irq) < ARM_IRQ2_BASE ? ARM_IC_IRQ_PENDING_1 : ((irq) < ARM_IRQBASIC_BASE ? ARM_IC_IRQ_PENDING_2 : ARM_IC_IRQ_BASIC_PENDING
//for (unsigned IRQIndex = 0; IRQIndex < IRQ_LINES; ++IRQIndex)
//{
// u32 nPendReg = ARM_IC_IRQ_PENDING(IRQIndex);
// u32 IRQIndexMask = ARM_IRQ_MASK(IRQIndex);
//
// if (read32(nPendReg) & IRQIndexMask)
// {
// IRQHandler* pHandler = IRQHandlers[IRQIndex];
// if (pHandler != 0)
// (*pHandler)(Params[IRQIndex]);
// else
// InterruptSystemDisableIRQ(IRQIndex);
// }
//}
unsigned IRQIndex;
u32 nPendReg;
u32 pendValue;
nPendReg = ARM_IC_IRQ_PENDING_1;
pendValue = read32(nPendReg);
for (IRQIndex = 0; IRQIndex < ARM_IRQ2_BASE; ++IRQIndex)
{
u32 IRQIndexMask = ARM_IRQ_MASK(IRQIndex);
if (pendValue & IRQIndexMask)
{
IRQHandler* pHandler = IRQHandlers[IRQIndex];
if (pHandler != 0)
(*pHandler)(Params[IRQIndex]);
else
InterruptSystemDisableIRQ(IRQIndex);
}
}
nPendReg = ARM_IC_IRQ_PENDING_2;
pendValue = read32(nPendReg);
for (;IRQIndex < ARM_IRQBASIC_BASE; ++IRQIndex)
{
u32 IRQIndexMask = ARM_IRQ_MASK(IRQIndex);
if (pendValue & IRQIndexMask)
{
IRQHandler* pHandler = IRQHandlers[IRQIndex];
if (pHandler != 0)
(*pHandler)(Params[IRQIndex]);
else
InterruptSystemDisableIRQ(IRQIndex);
}
}
nPendReg = ARM_IC_IRQ_BASIC_PENDING;
pendValue = read32(nPendReg);
for (;IRQIndex < IRQ_LINES; ++IRQIndex)
{
u32 IRQIndexMask = ARM_IRQ_MASK(IRQIndex);
if (pendValue & IRQIndexMask)
{
IRQHandler* pHandler = IRQHandlers[IRQIndex];
if (pHandler != 0)
(*pHandler)(Params[IRQIndex]);
else
InterruptSystemDisableIRQ(IRQIndex);
}
}
DataMemBarrier();
}

29
interrupt.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef interrupt_h
#define interrupt_h
#ifdef __cplusplus
extern "C" {
#endif
#include "rpi-interrupts.h"
#define EnableInterrupts() __asm volatile ("cpsie i")
#define DisableInterrupts() __asm volatile ("cpsid i")
typedef void IRQHandler(void* param);
void InterruptSystemInitialize();
void InterruptSystemConnectIRQ(unsigned IRQIndex, IRQHandler* handler, void* param);
void InterruptSystemDisconnectIRQ(unsigned IRQIndex);
void InterruptSystemEnableIRQ(unsigned IRQIndex);
void InterruptSystemDisableIRQ(unsigned IRQIndex);
void InterruptHandler(void);
#ifdef __cplusplus
}
#endif
#endif

78
kernel.h Normal file
View File

@ -0,0 +1,78 @@
//
// kernel.h
//
// Circle - A C++ bare metal environment for Raspberry Pi
// Copyright (C) 2014 R. Stange <rsta2@o2online.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _kernel_h
#define _kernel_h
//#include <circle/memory.h>
//#include <circle/actled.h>
//#include <circle/devicenameservice.h>
//#include <circle/koptions.h>
//#include <circle/screen.h>
//#include <circle/serial.h>
//#include <circle/exceptionhandler.h>
//#include <circle/interrupt.h>
//#include <circle/logger.h>
//#include <circle/types.h>
//#include <circle/gpiomanager.h>
#include "emmc.h"
//#include <SDCard/emmc.h>
//#include <circle/fs/fat/fatfs.h>
//#include <circle/multicore.h>
//#include <circle/usb/dwhcidevice.h>
#include "iec_bus.h"
#include "iec_commands.h"
enum TShutdownMode
{
ShutdownNone,
ShutdownHalt,
ShutdownReboot
};
class CKernel
{
public:
CKernel (void);
~CKernel (void);
bool Initialize (void);
TShutdownMode Run (void);
//private:
// static void KeyPressedHandler(const char *pString);
// static void ShutdownHandler(void);
//
// static void KeyStatusHandlerRaw(unsigned char ucModifiers, const unsigned char RawKeys[6]);
private:
// do not change this order
CEMMCDevice m_EMMC;
IEC_Commands m_IEC_Commands;
};
#endif

92402
kernel.lst Normal file

File diff suppressed because it is too large Load Diff

5519
kernel.map Normal file

File diff suppressed because it is too large Load Diff

235
linker.ld Normal file
View File

@ -0,0 +1,235 @@
/* Default linker script, for normal executables */
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm",
"elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib");
SECTIONS
{
/* Read-only sections, merged into text segment: */
PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x01F00000)); . = SEGMENT_START("text-segment", 0x01F00000);
.interp : { *(.interp) }
.note.gnu.build-id : { *(.note.gnu.build-id) }
.hash : { *(.hash) }
.gnu.hash : { *(.gnu.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rel.init : { *(.rel.init) }
.rela.init : { *(.rela.init) }
.rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) }
.rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
.rel.fini : { *(.rel.fini) }
.rela.fini : { *(.rela.fini) }
.rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) }
.rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
.rel.data.rel.ro : { *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*) }
.rela.data.rel.ro : { *(.rela.data.rel.ro* .rela.gnu.linkonce.d.rel.ro.*) }
.rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) }
.rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
.rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
.rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
.rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
.rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
.rel.ctors : { *(.rel.ctors) }
.rela.ctors : { *(.rela.ctors) }
.rel.dtors : { *(.rel.dtors) }
.rela.dtors : { *(.rela.dtors) }
.rel.got : { *(.rel.got) }
.rela.got : { *(.rela.got) }
.rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) }
.rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
.rel.iplt :
{
PROVIDE_HIDDEN (__rel_iplt_start = .);
*(.rel.iplt)
PROVIDE_HIDDEN (__rel_iplt_end = .);
}
.rela.iplt :
{
PROVIDE_HIDDEN (__rela_iplt_start = .);
*(.rela.iplt)
PROVIDE_HIDDEN (__rela_iplt_end = .);
}
.rel.plt :
{
*(.rel.plt)
}
.rela.plt :
{
*(.rela.plt)
}
.init :
{
KEEP (*(.init))
} =0
.plt : { *(.plt) }
.iplt : { *(.iplt) }
.text :
{
*(.text.startup .text.startup.*)
*(.text.unlikely .text.*_unlikely)
*(.text.exit .text.exit.*)
*(.text.hot .text.hot.*)
*(.text .stub .text.* .gnu.linkonce.t.*)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
*(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx)
} =0
.fini :
{
KEEP (*(.fini))
} =0
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) }
PROVIDE_HIDDEN (__exidx_start = .);
.ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) }
PROVIDE_HIDDEN (__exidx_end = .);
.eh_frame_hdr : { *(.eh_frame_hdr) }
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) }
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table
.gcc_except_table.*) }
/* These sections are generated by the Sun/Oracle C++ compiler. */
.exception_ranges : ONLY_IF_RO { *(.exception_ranges
.exception_ranges*) }
/* Adjust the address for the data segment. We want to adjust up to
the same address within the page on the next page up. */
/* . = ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)); */
/* Exception handling */
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) }
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
.exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) }
/* Thread Local Storage sections */
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
}
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
}
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array))
PROVIDE_HIDDEN (__fini_array_end = .);
}
.ctors :
{
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
KEEP (*crtbegin?.o(.ctors))
/* We don't want to include the .ctor section from
the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
}
.dtors :
{
KEEP (*crtbegin.o(.dtors))
KEEP (*crtbegin?.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
}
.jcr : { KEEP (*(.jcr)) }
.data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
.dynamic : { *(.dynamic) }
.got : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) }
.data :
{
__data_start = . ;
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
_edata = .; PROVIDE (edata = .);
__bss_start = .;
__bss_start__ = .;
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections.
FIXME: Why do we need it? When there is no .bss section, we don't
pad the .data section. */
. = ALIGN(. != 0 ? 32 / 8 : 1);
}
_bss_end__ = . ; __bss_end__ = . ;
. = ALIGN(32 / 8);
. = ALIGN(32 / 8);
__end__ = . ;
_end = .; PROVIDE (end = .);
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/* DWARF 3 */
.debug_pubtypes 0 : { *(.debug_pubtypes) }
.debug_ranges 0 : { *(.debug_ranges) }
.stack 0x80000 :
{
_stack = .;
*(.stack)
}
.ARM.attributes 0 : { KEEP (*(.ARM.attributes)) KEEP (*(.gnu.attributes)) }
.note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}

927
logo.h Normal file
View File

@ -0,0 +1,927 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
/* Generated by bin2c, do not edit manually */
const long int I__logo_png_size = 14454;
const unsigned char I__logo_png[14454] = {
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xC8, 0x08, 0x06, 0x00, 0x00, 0x00, 0x76, 0xE9, 0x93,
0x49, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0E, 0xC4, 0x00, 0x00, 0x0E,
0xC4, 0x01, 0x95, 0x2B, 0x0E, 0x1B, 0x00, 0x00, 0x20, 0x00, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9C,
0xED, 0xDD, 0x5F, 0xA8, 0x2D, 0x57, 0x9D, 0xE0, 0xF1, 0xDF, 0x6D, 0x1A, 0x9A, 0xA6, 0x65, 0x5E,
0x26, 0x3D, 0x86, 0x4E, 0x1E, 0x72, 0x7D, 0x90, 0x41, 0xD1, 0x16, 0x5B, 0x98, 0x41, 0x21, 0xEA,
0xD3, 0x18, 0x93, 0xAB, 0x18, 0xD1, 0x1B, 0xD2, 0xE0, 0x9C, 0x3B, 0x0C, 0x06, 0xC5, 0xA4, 0xC1,
0x8C, 0x83, 0xF9, 0xD3, 0x86, 0xAB, 0x26, 0x71, 0x26, 0x24, 0xD0, 0x46, 0x0C, 0x11, 0x99, 0x1C,
0x02, 0x23, 0xB9, 0x36, 0x1A, 0xE2, 0xC9, 0x1F, 0xDF, 0x8C, 0x81, 0xA4, 0x67, 0x40, 0x9D, 0x19,
0xC5, 0x66, 0xE8, 0x07, 0xAF, 0x0F, 0x51, 0x32, 0x43, 0x26, 0x4F, 0x69, 0x1A, 0x41, 0xC8, 0x3C,
0x9C, 0xF3, 0xDB, 0xE7, 0xEC, 0xDF, 0xA9, 0xDF, 0x5E, 0xAB, 0xAA, 0xD6, 0x5A, 0xB5, 0x56, 0xAD,
0xEF, 0xE7, 0xE5, 0xDE, 0xDA, 0xB5, 0x6B, 0xEF, 0x3A, 0x7B, 0xD7, 0xAE, 0xAA, 0xDF, 0x6F, 0xFD,
0xD6, 0x5A, 0x67, 0x44, 0xE4, 0x0D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0xF6, 0x07, 0x4B,
0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC8, 0x8F, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1D, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x07, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xD0, 0x01, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x80, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1D, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x07, 0x48, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xD0, 0x01, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x80, 0x04,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x07,
0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0x01, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x74, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x40, 0x07, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0x01, 0x12, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x74, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x20, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x07, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0x01, 0x12,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D,
0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x07, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xD0, 0x01, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1D, 0xF8, 0xC3, 0xA5, 0x77, 0x00, 0xEB, 0xF7, 0xC6, 0x1B, 0x6F, 0x44, 0x3D, 0xEF,
0xCC, 0x99, 0x33, 0x99, 0xF7, 0x64, 0x58, 0xEC, 0xFE, 0xA1, 0x6D, 0x4B, 0x1D, 0x5F, 0xA9, 0x70,
0x9C, 0x02, 0xA8, 0x5D, 0xEB, 0xE7, 0xD9, 0x1A, 0x71, 0xEE, 0x07, 0xDA, 0xF7, 0xF4, 0xD3, 0x4F,
0xEF, 0x5C, 0x7F, 0xC3, 0x0D, 0x37, 0x6C, 0x3D, 0xEF, 0xDC, 0xB9, 0x73, 0x5B, 0xEB, 0xF5, 0x3C,
0x90, 0xEA, 0x1C, 0x4B, 0x02, 0x00, 0xD9, 0xC4, 0x5E, 0xB4, 0x2E, 0x5C, 0xB8, 0x90, 0x79, 0x4F,
0x86, 0x71, 0x51, 0xED, 0xC3, 0x52, 0xC7, 0x17, 0x00, 0xF4, 0x26, 0xF5, 0x4D, 0x2A, 0x90, 0x8B,
0xDE, 0x1B, 0x7C, 0xFC, 0xE3, 0x1F, 0x17, 0x91, 0xE3, 0x00, 0x0C, 0x48, 0xC9, 0x3B, 0xCE, 0x34,
0xD0, 0xD7, 0x65, 0x7D, 0xDE, 0x63, 0x8F, 0x3D, 0x26, 0x22, 0x22, 0x07, 0x07, 0x07, 0x83, 0xEB,
0x53, 0x21, 0x01, 0x80, 0xE4, 0x6C, 0x60, 0x1D, 0xCA, 0x7A, 0x95, 0x56, 0xFB, 0xFE, 0x01, 0xBB,
0x70, 0xBC, 0x02, 0xA8, 0x1D, 0x89, 0x80, 0xF4, 0x38, 0xF7, 0x0F, 0xB3, 0x01, 0xD2, 0xFE, 0xFE,
0xBE, 0x88, 0x88, 0xEC, 0xED, 0xED, 0x45, 0x2D, 0x7F, 0xEF, 0x7B, 0xDF, 0xDB, 0xFA, 0x57, 0x03,
0x35, 0x20, 0x05, 0x7B, 0x3C, 0xD9, 0x40, 0xDF, 0x5B, 0xD6, 0xED, 0xEC, 0x71, 0xAD, 0xE6, 0x9E,
0x63, 0x49, 0x00, 0x20, 0x99, 0xDA, 0x03, 0xEB, 0xDA, 0xF7, 0x0F, 0x00, 0x80, 0x35, 0xD0, 0xEB,
0x2B, 0x89, 0x00, 0xB4, 0x86, 0x44, 0x00, 0x72, 0xB2, 0x81, 0xBD, 0xB7, 0xAC, 0x89, 0x2D, 0xED,
0x0A, 0x60, 0xCF, 0xA5, 0x73, 0x2B, 0x02, 0x48, 0x00, 0x60, 0xB6, 0xDA, 0x03, 0xEB, 0xDA, 0xF7,
0x0F, 0x00, 0x80, 0x35, 0x22, 0x11, 0x80, 0x52, 0xC6, 0xB6, 0xFC, 0x87, 0x90, 0x08, 0x40, 0x4E,
0xF6, 0xB8, 0xF2, 0x2A, 0x05, 0xBC, 0x65, 0x5B, 0x11, 0x30, 0x16, 0x09, 0x00, 0x4C, 0x56, 0x7B,
0x60, 0x5D, 0xFB, 0xFE, 0x01, 0x00, 0xD0, 0x03, 0x12, 0x01, 0x68, 0x15, 0x89, 0x00, 0xE4, 0xE4,
0x8D, 0x09, 0xA0, 0x34, 0xD0, 0xD7, 0x7F, 0x53, 0x9D, 0x43, 0x49, 0x00, 0x60, 0xB4, 0xDA, 0x03,
0xEB, 0xDA, 0xF7, 0x0F, 0x00, 0x80, 0x1E, 0x91, 0x08, 0x40, 0x2E, 0xB1, 0x2D, 0xFF, 0x63, 0x2B,
0x01, 0x14, 0x89, 0x00, 0xCC, 0x11, 0x1A, 0xFC, 0xCF, 0x76, 0x05, 0xD0, 0x31, 0x01, 0x52, 0x97,
0xFE, 0x2B, 0x12, 0x00, 0x88, 0x56, 0x7B, 0x60, 0x5D, 0xFB, 0xFE, 0x01, 0x00, 0x00, 0x12, 0x01,
0x68, 0x17, 0x89, 0x00, 0x4C, 0x11, 0xEA, 0xF3, 0xEF, 0x3D, 0xDF, 0x9A, 0x5B, 0xFA, 0xAF, 0x48,
0x00, 0x20, 0xA8, 0xF6, 0xC0, 0x3A, 0xD5, 0xFE, 0xB5, 0x3E, 0x05, 0x4C, 0x2D, 0xDF, 0x4B, 0xE9,
0xCF, 0x71, 0xEC, 0xDF, 0xAD, 0x53, 0xAB, 0x58, 0xDE, 0x7E, 0xDB, 0x1B, 0xD3, 0xB1, 0xD3, 0x47,
0xA6, 0xDE, 0x7E, 0x8E, 0xDC, 0xDF, 0xCD, 0xDC, 0x63, 0xB0, 0xF5, 0xDF, 0x60, 0x6E, 0xB5, 0xFC,
0xC6, 0x63, 0xD4, 0x7E, 0xAC, 0xE5, 0xD0, 0xE3, 0xDF, 0x3C, 0x17, 0x89, 0x00, 0xA4, 0x92, 0xAB,
0xE5, 0x1F, 0x48, 0x21, 0xD4, 0xE7, 0xDF, 0x3E, 0x6E, 0x2B, 0x06, 0xB4, 0x22, 0x40, 0x97, 0x75,
0x70, 0xC0, 0xA9, 0x48, 0x00, 0xC0, 0xD5, 0x4B, 0xE0, 0x9F, 0x9B, 0x2D, 0xE7, 0xC9, 0xF5, 0xFA,
0xBD, 0x65, 0xA3, 0xC7, 0xFE, 0xDD, 0x36, 0xF0, 0xD7, 0x2C, 0xBE, 0x47, 0x4F, 0xBE, 0x76, 0x2E,
0x56, 0x15, 0x3A, 0xDE, 0x52, 0x6F, 0x5F, 0xB3, 0xDA, 0x8F, 0xC1, 0x52, 0xBF, 0x41, 0x7E, 0xE3,
0xF9, 0xF5, 0xF8, 0x59, 0xF4, 0xF0, 0x37, 0x93, 0x08, 0x00, 0x80, 0x63, 0xA1, 0xE9, 0x02, 0xF5,
0xDE, 0xD0, 0x9B, 0x25, 0x20, 0x84, 0x04, 0x00, 0x4E, 0xA9, 0x3D, 0xB0, 0xCE, 0xBD, 0x7F, 0xA9,
0x6F, 0x3C, 0x6C, 0xB6, 0x39, 0xF7, 0xEB, 0xD7, 0x22, 0xF7, 0x0D, 0x5C, 0xAA, 0xBF, 0xDB, 0x1B,
0x60, 0x25, 0x96, 0xCD, 0xC2, 0x96, 0xDE, 0x7E, 0x8A, 0xDA, 0x8F, 0xC1, 0xD8, 0xFD, 0x8B, 0xBD,
0xE0, 0x4D, 0xF9, 0x0D, 0x7E, 0xFE, 0xA9, 0xEB, 0x45, 0x44, 0xE4, 0xC1, 0x8F, 0x3C, 0x1D, 0xDC,
0x66, 0xEA, 0x6F, 0x5C, 0xF7, 0xFF, 0xFE, 0xBB, 0x2E, 0x8B, 0x88, 0xC8, 0x9D, 0xF7, 0xBD, 0x25,
0xEA, 0xF5, 0x5B, 0x72, 0xF2, 0xB3, 0xB0, 0x7F, 0x87, 0x57, 0xCA, 0xB8, 0xEB, 0x79, 0x2D, 0x7C,
0x16, 0xBB, 0xBE, 0xFF, 0x5F, 0x5E, 0xBE, 0x5B, 0x44, 0x44, 0xDE, 0x7E, 0xF6, 0xAB, 0xD1, 0xAF,
0xD7, 0xC2, 0xDF, 0x9C, 0x0A, 0x89, 0x00, 0x4C, 0x95, 0x7B, 0x0C, 0x00, 0x8B, 0xAE, 0x00, 0xC8,
0xC1, 0x36, 0x16, 0x79, 0x89, 0x80, 0xD0, 0xAC, 0x01, 0xA1, 0x73, 0x28, 0x09, 0x00, 0x6C, 0xF4,
0x1E, 0xF8, 0x4F, 0xC5, 0x8D, 0xCA, 0x3C, 0xB7, 0xDC, 0xB8, 0xBD, 0xFC, 0xE8, 0xF7, 0x97, 0xD9,
0x0F, 0x9C, 0xF6, 0xA1, 0xF7, 0x7E, 0x77, 0xF0, 0xF1, 0x1F, 0xBE, 0xF4, 0xC9, 0xC2, 0x7B, 0xB2,
0xCD, 0x9E, 0x0B, 0x52, 0xFE, 0x06, 0xF5, 0xB5, 0x6E, 0xFF, 0xC1, 0x0D, 0x5B, 0xFF, 0x6A, 0x42,
0xE0, 0xA1, 0x8F, 0x3E, 0x93, 0xEC, 0x3D, 0xD4, 0x1D, 0xF7, 0x9E, 0x3D, 0xFA, 0xDF, 0xAF, 0x44,
0xC4, 0x4F, 0x04, 0xB4, 0x4A, 0x6F, 0xB4, 0x2F, 0x9C, 0xBD, 0x52, 0x44, 0x44, 0x1E, 0xBB, 0xFC,
0xCA, 0xD6, 0xE3, 0xDE, 0x0D, 0xB9, 0x3E, 0x5F, 0xCC, 0xF3, 0x5A, 0xA4, 0xBF, 0xA5, 0xDB, 0xFF,
0x72, 0x7B, 0xD9, 0xFE, 0x96, 0xEE, 0xBB, 0x73, 0x9D, 0xC7, 0xC0, 0x58, 0xB5, 0x5C, 0xDF, 0x01,
0x20, 0x27, 0x5B, 0xEA, 0x6F, 0x03, 0x7C, 0x6F, 0xCC, 0x00, 0xDD, 0xCE, 0xBB, 0xFF, 0x09, 0x0D,
0x16, 0x48, 0x02, 0x00, 0xD5, 0x06, 0xD6, 0xAA, 0xD6, 0xFD, 0xD3, 0x80, 0x20, 0x47, 0x80, 0xB0,
0x66, 0x1A, 0xF0, 0xBF, 0xFB, 0xCD, 0xEF, 0x10, 0x11, 0x91, 0x3F, 0xFB, 0xCA, 0xBF, 0xDF, 0x5A,
0xFF, 0xEE, 0x37, 0x7F, 0x5B, 0x44, 0x44, 0x7E, 0xF6, 0x7F, 0x7E, 0x21, 0x22, 0x24, 0x04, 0x96,
0xE0, 0x05, 0xFE, 0x6A, 0xA9, 0xA4, 0x97, 0x06, 0x47, 0x4A, 0x5B, 0xCE, 0x53, 0xB0, 0xBF, 0x67,
0xA5, 0x15, 0x00, 0xFA, 0x38, 0x09, 0xBF, 0xF1, 0x34, 0x70, 0xBF, 0x70, 0xF1, 0x8B, 0x87, 0xFF,
0x3A, 0x89, 0x00, 0xB5, 0x09, 0xFC, 0xCD, 0xF6, 0x2D, 0xB2, 0xC7, 0xEC, 0x0B, 0xCF, 0xFF, 0x64,
0xF0, 0x79, 0xA1, 0xDF, 0x5C, 0xAF, 0x6C, 0x92, 0x08, 0xF0, 0x30, 0x06, 0x00, 0x5A, 0xE4, 0x05,
0xFC, 0xD6, 0xD8, 0x16, 0xFF, 0xD0, 0x39, 0x93, 0x04, 0x40, 0xC7, 0x6A, 0x0D, 0xAC, 0x55, 0xAD,
0xFB, 0xA7, 0x81, 0x42, 0x68, 0xFD, 0x9C, 0x44, 0xC0, 0xC3, 0x1F, 0x7B, 0x76, 0xF0, 0xF1, 0x5B,
0x9F, 0xFC, 0xF0, 0xE4, 0xD7, 0xAC, 0x85, 0x17, 0xF8, 0xAB, 0xCD, 0xE3, 0x7F, 0xFD, 0xED, 0xA3,
0x47, 0x7E, 0x51, 0x60, 0xAF, 0xD0, 0x12, 0x0D, 0xFC, 0x8F, 0x5B, 0xCE, 0x0F, 0xDD, 0x79, 0xDF,
0xFC, 0xD7, 0xB6, 0x01, 0xBF, 0xFE, 0x6B, 0x1F, 0x1F, 0xC3, 0x9E, 0xCB, 0x52, 0x26, 0x2E, 0x5A,
0xA4, 0x81, 0x7F, 0x68, 0xBD, 0x4D, 0x04, 0xAC, 0xC1, 0xB5, 0x1F, 0x78, 0x8F, 0x59, 0xFE, 0x95,
0xF3, 0x4C, 0x00, 0xC0, 0xDA, 0xD9, 0xC0, 0xDE, 0x9B, 0x2E, 0xD0, 0x2E, 0xDB, 0xE7, 0x6B, 0x42,
0x20, 0xB6, 0x91, 0x82, 0x04, 0x40, 0x87, 0x6A, 0x0D, 0xAC, 0x55, 0x2D, 0xFB, 0xE7, 0xF5, 0xC5,
0x8E, 0x0D, 0x00, 0x34, 0x11, 0xF0, 0xDA, 0x93, 0xF1, 0xEF, 0xE9, 0x05, 0xFE, 0x9F, 0xFB, 0xFE,
0x75, 0x87, 0xFF, 0xB9, 0xF1, 0x70, 0x7D, 0x8B, 0x89, 0x00, 0x6D, 0xF9, 0xD7, 0x96, 0xFD, 0x3F,
0x0B, 0x3C, 0x5F, 0x9F, 0xA7, 0xDB, 0x51, 0x09, 0x90, 0x5F, 0xA8, 0x15, 0xF2, 0xB9, 0x17, 0x3F,
0x51, 0x68, 0x4F, 0x76, 0xB3, 0x81, 0x7F, 0x4A, 0x5E, 0xC0, 0x3F, 0x25, 0xF0, 0xF7, 0x68, 0x79,
0xF7, 0x8F, 0x8F, 0x5A, 0xC4, 0xDF, 0x7F, 0xCF, 0xD7, 0x92, 0xBD, 0x76, 0xCD, 0x6C, 0xE0, 0x6F,
0x2B, 0x01, 0xEC, 0x72, 0x28, 0x51, 0xD0, 0x02, 0xFD, 0xAE, 0x43, 0xDD, 0x69, 0x74, 0xBD, 0x4D,
0x10, 0x00, 0x88, 0x53, 0x7A, 0x0C, 0x00, 0x20, 0x25, 0x1B, 0xC8, 0x7B, 0x7D, 0xFE, 0xBD, 0xF5,
0x63, 0xAB, 0xA4, 0x48, 0x00, 0x74, 0xA4, 0x96, 0xC0, 0xDA, 0x53, 0xDB, 0xFE, 0x69, 0xF6, 0xCC,
0xF6, 0x09, 0xB6, 0x6C, 0xA0, 0x30, 0xA7, 0xA5, 0xD0, 0xF3, 0x8D, 0x1B, 0x9F, 0x13, 0x91, 0xE3,
0x44, 0xC0, 0xAD, 0x0D, 0x56, 0x1F, 0x6B, 0xCB, 0xFF, 0x2D, 0x8F, 0x1C, 0x06, 0xF6, 0x37, 0x7C,
0x6B, 0xF7, 0xF3, 0x35, 0xE0, 0x7F, 0xF4, 0x33, 0xEF, 0x38, 0x7A, 0x24, 0x6D, 0x25, 0x80, 0x9E,
0x4C, 0xA7, 0x8E, 0xDC, 0x6E, 0xFB, 0x5F, 0x95, 0xDE, 0x3E, 0xA5, 0xB1, 0xE5, 0xC7, 0x2F, 0x7C,
0xF9, 0x8E, 0x4C, 0x7B, 0x32, 0xCC, 0x0E, 0x96, 0x97, 0x63, 0x3F, 0xEC, 0x60, 0x7F, 0x0F, 0x1E,
0x7D, 0x2D, 0x5A, 0xC9, 0x13, 0x3A, 0x0F, 0x0C, 0xF1, 0xBE, 0x5B, 0x2F, 0x43, 0xDF, 0x4B, 0xF7,
0x02, 0x0D, 0xF4, 0xAF, 0xF8, 0xD4, 0xE1, 0x2D, 0xC8, 0x85, 0xC7, 0x87, 0x97, 0xD7, 0x90, 0x00,
0x50, 0xB6, 0xAF, 0xBF, 0xFD, 0xCD, 0xE9, 0x7A, 0x2A, 0x02, 0x00, 0xA0, 0x3F, 0x5E, 0xA0, 0xEF,
0x75, 0x09, 0xF0, 0x02, 0xFF, 0xD8, 0x44, 0x00, 0x09, 0x80, 0x0E, 0xD4, 0x16, 0x58, 0x87, 0xD4,
0xB6, 0x7F, 0x7A, 0x33, 0x6E, 0x4B, 0xFF, 0x6D, 0xC0, 0xA0, 0xEB, 0x6D, 0xE2, 0xE0, 0xC2, 0x93,
0xBB, 0x07, 0xE2, 0x10, 0x39, 0xDD, 0xF2, 0xFF, 0xC6, 0xEF, 0xFE, 0xF3, 0xE1, 0x6B, 0xFD, 0xD1,
0x7F, 0xDC, 0xF9, 0xFC, 0x16, 0x2B, 0x01, 0xD4, 0xD3, 0x9F, 0xFE, 0x2B, 0x11, 0x11, 0xB9, 0xE1,
0x5B, 0x7F, 0x33, 0xF8, 0x38, 0xF2, 0x9B, 0xDB, 0xEF, 0x78, 0x73, 0x8C, 0x07, 0x06, 0x9B, 0x99,
0xFB, 0xFA, 0x96, 0x17, 0xF8, 0xB7, 0x3A, 0x80, 0x5A, 0xCE, 0x41, 0x0D, 0x6B, 0xA4, 0x81, 0x7E,
0x70, 0xF9, 0x62, 0xA9, 0x3D, 0x2A, 0xC7, 0xFE, 0xE6, 0xEC, 0x60, 0x80, 0xAD, 0x1D, 0xBB, 0x40,
0x2D, 0x68, 0xF9, 0x47, 0xCB, 0x6C, 0xA0, 0xEF, 0x4D, 0xFF, 0xA7, 0xB4, 0x11, 0x69, 0xEA, 0xF8,
0x28, 0x24, 0x00, 0x56, 0xAC, 0xB5, 0xC0, 0xBF, 0x36, 0xF6, 0xF3, 0x4B, 0xD9, 0xA2, 0xEF, 0xBE,
0xE7, 0x51, 0xE0, 0x7F, 0xDB, 0xB3, 0xCF, 0x8B, 0x88, 0xC8, 0xD7, 0x8F, 0xE2, 0x7B, 0x4D, 0x04,
0x68, 0x25, 0x40, 0x4B, 0xEC, 0x28, 0xFF, 0x4A, 0x5B, 0xF8, 0x6D, 0x25, 0x80, 0x57, 0xEA, 0x3F,
0xB7, 0x2B, 0x80, 0x4E, 0xD9, 0x33, 0xD5, 0xDC, 0xDF, 0x4F, 0x8D, 0xBF, 0xBF, 0x50, 0xAB, 0xA4,
0x75, 0xDD, 0xFB, 0xFE, 0x56, 0x44, 0x44, 0x9E, 0x7B, 0xF1, 0xFE, 0x6C, 0xFB, 0x74, 0x92, 0xD7,
0xD7, 0xFF, 0xDA, 0x2F, 0x1D, 0xBE, 0xBF, 0x4D, 0x04, 0xE8, 0xF3, 0xA6, 0x8C, 0x05, 0xA0, 0xBF,
0x6F, 0x2D, 0xCB, 0xD7, 0xD7, 0xB6, 0xCB, 0x0F, 0x7E, 0xE9, 0xF0, 0x7B, 0x7C, 0x48, 0xC6, 0x07,
0xE7, 0xDE, 0xDF, 0x63, 0x1F, 0x5F, 0xDB, 0x18, 0x01, 0xDE, 0x0D, 0xF7, 0x03, 0x17, 0x5F, 0x16,
0x11, 0x91, 0x2F, 0xDC, 0x73, 0xF5, 0xE0, 0xB2, 0xDD, 0xBE, 0xE5, 0x81, 0xE0, 0x34, 0x39, 0xA5,
0x83, 0x00, 0x6A, 0xA9, 0xBF, 0x2E, 0xB7, 0x9A, 0xBC, 0x02, 0x00, 0xA4, 0x17, 0x5B, 0xFA, 0xAF,
0xC6, 0x36, 0x1C, 0x90, 0x00, 0x58, 0x21, 0x02, 0xFF, 0x34, 0x6C, 0x4B, 0x3E, 0xC6, 0xB1, 0xA3,
0xFD, 0x2B, 0x2D, 0xE9, 0xF7, 0x06, 0x01, 0x3C, 0x78, 0xF5, 0xB0, 0x22, 0xE0, 0xB7, 0x9B, 0x41,
0x00, 0xB7, 0x5F, 0xE7, 0x96, 0x1B, 0x0F, 0xBB, 0x02, 0xFC, 0x6E, 0xE6, 0xFE, 0xD9, 0xA9, 0x54,
0x7A, 0xA6, 0x81, 0xBF, 0xED, 0xE3, 0xAF, 0x01, 0xBF, 0x75, 0x9C, 0x08, 0xC8, 0x33, 0x26, 0x80,
0x06, 0x43, 0xA1, 0x80, 0xD8, 0x4B, 0x04, 0xA8, 0x31, 0x15, 0x0A, 0x5A, 0xEA, 0xFF, 0xE0, 0x1B,
0x4F, 0xEF, 0x7C, 0xCD, 0x1C, 0xD6, 0x1A, 0xF8, 0x2B, 0xDB, 0xB7, 0x5F, 0xD9, 0x40, 0xDF, 0x2E,
0xDB, 0xED, 0xF7, 0xD3, 0xEF, 0x5A, 0x76, 0xFA, 0xDB, 0xB2, 0xA3, 0xFF, 0x7B, 0xCB, 0xDE, 0xF4,
0x80, 0x00, 0x76, 0x63, 0x0C, 0x00, 0xAC, 0x81, 0x37, 0xC8, 0x9F, 0xB2, 0x89, 0x81, 0xCD, 0x2C,
0x3B, 0x23, 0x13, 0x02, 0x24, 0x00, 0x56, 0x24, 0x57, 0xE0, 0x6F, 0x0F, 0xBE, 0xDE, 0x12, 0x0A,
0x5E, 0x17, 0x00, 0x3B, 0x2D, 0x98, 0xB7, 0x2C, 0xF2, 0xA7, 0xF1, 0xEF, 0x75, 0xD4, 0xD2, 0x6F,
0x5B, 0xFE, 0x5B, 0x14, 0x1A, 0xED, 0x3F, 0xC4, 0xDD, 0xEE, 0x28, 0x31, 0xF0, 0x77, 0xFF, 0x34,
0xE9, 0x65, 0x37, 0x62, 0x2B, 0x02, 0xF4, 0x79, 0x9A, 0x75, 0x1D, 0x7B, 0xFC, 0xA7, 0xDE, 0x3E,
0x27, 0x1B, 0xF0, 0x6B, 0x80, 0xEF, 0x25, 0x02, 0x72, 0x39, 0x3D, 0xBA, 0xFF, 0x61, 0xAB, 0xA8,
0x26, 0x06, 0x6C, 0x8B, 0xB9, 0x26, 0x02, 0xF4, 0xF1, 0x94, 0x49, 0x3B, 0x7D, 0x6D, 0xFD, 0x3D,
0x3F, 0x95, 0xB0, 0x12, 0xC8, 0x4E, 0x11, 0xA7, 0xE6, 0x54, 0x32, 0xB4, 0xE0, 0xD5, 0xC7, 0x7F,
0x2F, 0x22, 0xC7, 0xA5, 0xFE, 0xDE, 0xF2, 0x1A, 0xD8, 0x96, 0xFE, 0x07, 0xFF, 0xEB, 0xCF, 0x45,
0x44, 0xE4, 0xF6, 0xBF, 0x7C, 0xE7, 0xE0, 0xB2, 0x3E, 0xFF, 0x87, 0x2F, 0x15, 0xDD, 0x4D, 0x00,
0xC0, 0x02, 0xEC, 0x28, 0xFF, 0xA1, 0xB1, 0x00, 0xEC, 0xBD, 0xA4, 0x17, 0xE8, 0x87, 0x1A, 0x3E,
0x48, 0x00, 0xAC, 0x40, 0xEE, 0xC0, 0xDF, 0xBE, 0x9E, 0xF7, 0xF8, 0xDA, 0x78, 0x5D, 0x00, 0xB4,
0xA5, 0xD0, 0x9B, 0x37, 0x7C, 0x4C, 0x57, 0x01, 0x6F, 0xD4, 0xFF, 0x96, 0x03, 0x7F, 0x65, 0x03,
0x78, 0xED, 0xDB, 0x6F, 0x2B, 0x02, 0x74, 0x50, 0x40, 0x75, 0x3C, 0xE8, 0xDF, 0x21, 0x9D, 0x0D,
0x40, 0xC7, 0x0A, 0xD8, 0xBC, 0xEE, 0x7F, 0xF8, 0x1F, 0xC9, 0xF6, 0x15, 0xC3, 0x4A, 0x07, 0xFE,
0x96, 0x97, 0xB9, 0x0E, 0xCD, 0x02, 0xA0, 0x89, 0x00, 0x4D, 0x1C, 0x8C, 0x69, 0xE9, 0xB1, 0x09,
0xBF, 0x6B, 0x8F, 0x1E, 0xB7, 0xBF, 0xFB, 0x14, 0x6C, 0xCB, 0x7F, 0xCE, 0xD9, 0x0D, 0x96, 0x32,
0xF4, 0xD9, 0x6F, 0x46, 0xFD, 0x37, 0x83, 0xFD, 0x85, 0x06, 0xFF, 0x5B, 0x43, 0x8B, 0x9D, 0x17,
0xF8, 0x2B, 0x5B, 0x19, 0x00, 0xCC, 0x35, 0xB7, 0xFB, 0x9B, 0x67, 0x6A, 0x52, 0x3B, 0x17, 0x5A,
0xFE, 0xD1, 0x22, 0x1B, 0xE0, 0xDB, 0xB1, 0x00, 0x6C, 0xB5, 0xAA, 0xD7, 0x15, 0xC0, 0x26, 0x02,
0x42, 0x5D, 0xE6, 0x48, 0x00, 0x34, 0xAC, 0x74, 0xE0, 0x6F, 0xDF, 0x67, 0xED, 0x89, 0x00, 0xAF,
0x0B, 0x80, 0x06, 0x00, 0x1A, 0x10, 0x28, 0x2F, 0x0B, 0x97, 0xF2, 0x62, 0xA3, 0x83, 0xFE, 0x79,
0x89, 0x83, 0x16, 0xD8, 0x80, 0xDF, 0x8E, 0x11, 0x10, 0x5A, 0x9F, 0x9A, 0x77, 0x73, 0xE4, 0x8D,
0xBC, 0x1A, 0xDA, 0xAE, 0xD4, 0xF6, 0x25, 0x95, 0xAE, 0x04, 0x98, 0x5A, 0x0A, 0xFF, 0xBE, 0x3F,
0x3A, 0x1C, 0x50, 0x22, 0xC5, 0xB4, 0x7A, 0xFA, 0xFB, 0xD6, 0xBE, 0xFE, 0xDE, 0xEF, 0x7E, 0x8C,
0x1E, 0x02, 0xFE, 0x5D, 0x42, 0xD3, 0xFC, 0x79, 0xD3, 0x04, 0xB6, 0xCC, 0x0B, 0xE8, 0x6D, 0xE0,
0xAF, 0xA8, 0x00, 0x40, 0xED, 0x4A, 0x54, 0xA3, 0x01, 0xBD, 0xB0, 0xF7, 0x7A, 0x5E, 0x6C, 0x15,
0x3B, 0x26, 0x40, 0xEC, 0x58, 0x00, 0x24, 0x00, 0x1A, 0x54, 0x4B, 0x1F, 0x7F, 0x7D, 0x5F, 0x0D,
0x64, 0x6A, 0x0A, 0x58, 0x52, 0x7A, 0xF8, 0xFA, 0x0F, 0x8A, 0x88, 0xC8, 0x35, 0x47, 0xCB, 0xBF,
0xBE, 0xE5, 0x4F, 0x44, 0xC4, 0xEF, 0x12, 0x60, 0x1F, 0x7F, 0xED, 0xC9, 0xF9, 0xFB, 0x60, 0x47,
0xFB, 0x6F, 0x61, 0xF4, 0x7F, 0xED, 0xC3, 0x6F, 0x2B, 0x01, 0x6C, 0x40, 0x7F, 0xAA, 0x22, 0xE0,
0xC6, 0xDD, 0xD3, 0xFD, 0x1D, 0x8F, 0x0D, 0xF0, 0x17, 0xB3, 0xF6, 0x4F, 0xD9, 0x2C, 0xA9, 0xFE,
0xBE, 0x62, 0x7F, 0x57, 0x4B, 0x6F, 0x9F, 0x92, 0x17, 0xE8, 0xDB, 0x3E, 0xFF, 0xBA, 0xAC, 0x17,
0x98, 0x5C, 0x2D, 0x2A, 0xDE, 0x85, 0xCC, 0xEB, 0x9F, 0xFF, 0xE2, 0xEF, 0x3E, 0x7D, 0xF4, 0xBF,
0xF9, 0x09, 0x80, 0x14, 0x2D, 0xFD, 0xA1, 0xC1, 0xFD, 0xBC, 0x44, 0xC0, 0x5A, 0x47, 0xFF, 0x57,
0xA1, 0x96, 0xB8, 0x4D, 0x9F, 0xC6, 0xA3, 0x01, 0x18, 0xD7, 0x44, 0x03, 0x7C, 0x4B, 0x13, 0x05,
0x54, 0x00, 0xA0, 0x56, 0xB5, 0x07, 0xFE, 0xF6, 0x7C, 0x12, 0x5A, 0x06, 0x6A, 0xE2, 0xB5, 0xF0,
0xEB, 0xBD, 0xA0, 0xAD, 0x04, 0xD0, 0xD8, 0xCB, 0x9B, 0x0E, 0x30, 0x84, 0x04, 0x40, 0x43, 0x4A,
0x05, 0xFE, 0xB9, 0xA6, 0xF4, 0x5A, 0x2B, 0x9D, 0x0E, 0x50, 0xD9, 0xAE, 0x02, 0x73, 0x2E, 0x3A,
0x36, 0xD0, 0xB7, 0x2D, 0xFF, 0x35, 0x27, 0x02, 0xB4, 0x74, 0x5F, 0xFB, 0xEC, 0xEB, 0xE8, 0xFD,
0x9A, 0x00, 0xD0, 0x92, 0xFE, 0x73, 0x57, 0x6C, 0x4F, 0xFB, 0xA7, 0x83, 0x00, 0x6A, 0x97, 0x01,
0xDD, 0xEE, 0xDD, 0x6F, 0xFE, 0xF6, 0xF6, 0xEB, 0xFE, 0xB3, 0x34, 0x09, 0x80, 0x9E, 0x8D, 0x9D,
0x06, 0x50, 0x03, 0xFF, 0x3B, 0xFE, 0xCD, 0xCF, 0x8E, 0xFE, 0x3D, 0x0C, 0xD0, 0x52, 0xCF, 0xD7,
0xAE, 0xA5, 0xFB, 0x77, 0xDC, 0x3B, 0xDC, 0x97, 0x5F, 0xFB, 0xE5, 0x5B, 0x2F, 0x66, 0x1C, 0x44,
0x6F, 0x4A, 0xCB, 0xBF, 0xD7, 0xC2, 0xAF, 0x8F, 0x6B, 0xA0, 0x6F, 0xFB, 0xFA, 0xAF, 0x7D, 0x44,
0xF8, 0xD0, 0xA8, 0xFE, 0x6B, 0xBA, 0x51, 0xB7, 0x63, 0x00, 0xC4, 0x3E, 0x9F, 0x0A, 0x00, 0xD4,
0xA2, 0xF6, 0xC0, 0x9F, 0x7B, 0x56, 0xAC, 0x41, 0xA8, 0x85, 0xDF, 0x8E, 0x11, 0xE0, 0x5D, 0x3F,
0x63, 0x13, 0x01, 0x24, 0x00, 0x1A, 0x50, 0xBA, 0xC5, 0x7F, 0x6C, 0x4B, 0x7E, 0xAE, 0xFE, 0x65,
0xB5, 0xD0, 0x16, 0x7F, 0xCB, 0x06, 0xFE, 0xA7, 0x1E, 0x7F, 0xEA, 0x30, 0x11, 0x30, 0x54, 0x01,
0xE0, 0x95, 0xF0, 0x87, 0x02, 0x7E, 0xEF, 0x79, 0x35, 0x3A, 0x0E, 0xF8, 0x77, 0xB7, 0xE8, 0xC7,
0x96, 0xF8, 0x6B, 0xE0, 0xAF, 0xAF, 0xBB, 0xA2, 0x18, 0x01, 0x0E, 0xAF, 0x25, 0xDC, 0x6B, 0x59,
0x4F, 0x11, 0x30, 0xDB, 0x96, 0xFF, 0x39, 0x25, 0xFF, 0x21, 0x76, 0xB6, 0x03, 0xB5, 0xD6, 0x0A,
0x00, 0x9B, 0x28, 0x0A, 0x05, 0xFA, 0x9B, 0xB1, 0x01, 0x56, 0xD0, 0x15, 0x20, 0x16, 0x15, 0x00,
0x28, 0x2D, 0x14, 0x30, 0xD4, 0x9E, 0x00, 0x98, 0xBB, 0x7F, 0xB9, 0x13, 0x08, 0xCC, 0x34, 0x04,
0x91, 0x70, 0xEC, 0x66, 0x03, 0x7D, 0xFD, 0x5D, 0xEA, 0xBF, 0x1A, 0x0B, 0x9E, 0x3B, 0x77, 0x2E,
0xC9, 0xFE, 0x90, 0x00, 0xA8, 0xD8, 0x52, 0xA5, 0xFE, 0x63, 0x4F, 0x56, 0x6B, 0x4F, 0x00, 0x58,
0xD7, 0x3C, 0xFA, 0x8F, 0x22, 0x22, 0xF2, 0xF0, 0xA3, 0x87, 0x5D, 0x03, 0x6E, 0x7D, 0xE6, 0x47,
0x5B, 0xEB, 0xB5, 0xCB, 0x80, 0x38, 0x89, 0x83, 0x5D, 0x5A, 0xEE, 0xDB, 0xEF, 0xD1, 0x80, 0xDD,
0xB2, 0xD3, 0xFC, 0x85, 0x1E, 0xF7, 0x5E, 0x07, 0xE3, 0x79, 0x2D, 0xFF, 0x5E, 0xA9, 0x7F, 0x6D,
0x6C, 0x0B, 0xBA, 0x06, 0xD2, 0x63, 0xE7, 0xC1, 0xDD, 0x25, 0x67, 0xE0, 0xEF, 0xB1, 0x7F, 0xCF,
0x1A, 0x2A, 0x00, 0xF6, 0xF7, 0xF7, 0x4F, 0x65, 0xEB, 0xBC, 0x69, 0x01, 0xBD, 0xF5, 0xFA, 0xEF,
0xFE, 0xFE, 0x7E, 0x73, 0xD5, 0x01, 0xA1, 0xE9, 0xFF, 0x00, 0x2C, 0x83, 0xCA, 0x01, 0x94, 0x60,
0x5B, 0xF6, 0x6D, 0x1F, 0x7F, 0xAF, 0xF4, 0xFF, 0xE0, 0xE0, 0x60, 0x70, 0x7D, 0x2A, 0x24, 0x00,
0x2A, 0x54, 0x5B, 0x1F, 0xFF, 0x5E, 0xD9, 0x96, 0x40, 0x0D, 0xFC, 0x63, 0xE9, 0xF3, 0x5F, 0xFB,
0x17, 0xA7, 0xA7, 0x01, 0x1C, 0x3B, 0x98, 0x5F, 0xCB, 0x83, 0xFF, 0x69, 0x0B, 0xBF, 0x06, 0xF0,
0xB6, 0x32, 0xC0, 0x06, 0xF6, 0xFE, 0xE3, 0x32, 0xF8, 0xF8, 0x5C, 0xF6, 0x24, 0xDB, 0xDA, 0xF6,
0x39, 0xD8, 0x44, 0xC0, 0xE9, 0xF5, 0x87, 0xFF, 0xEA, 0x5C, 0xE5, 0xB9, 0x83, 0x32, 0x3B, 0x5D,
0x5E, 0x6B, 0x2D, 0xE4, 0xA1, 0x41, 0x0D, 0xBD, 0xE9, 0x0B, 0xD7, 0x32, 0x0D, 0xA0, 0xB6, 0x60,
0xFC, 0x78, 0x64, 0x9F, 0x7E, 0x4D, 0x04, 0xA4, 0x18, 0xD4, 0x71, 0x29, 0xFA, 0x1B, 0xD1, 0xA4,
0x9B, 0xED, 0x12, 0x60, 0x97, 0xF5, 0xF9, 0x40, 0x29, 0x35, 0x5E, 0x83, 0xA6, 0xB0, 0xA5, 0xD1,
0xB5, 0xCD, 0x06, 0xD0, 0xFB, 0x3D, 0x75, 0xAF, 0x6C, 0x55, 0xB5, 0x37, 0x78, 0x9F, 0x5D, 0xB6,
0x5D, 0x01, 0xBC, 0xF1, 0xA2, 0xA6, 0xDE, 0x0F, 0x91, 0x00, 0xA8, 0x48, 0xEE, 0x51, 0xFD, 0x63,
0x5F, 0xD7, 0x1E, 0x7C, 0x9E, 0xB5, 0xB6, 0xFC, 0xC7, 0x0E, 0xFE, 0xA5, 0x5D, 0x03, 0xEC, 0xB4,
0x7F, 0xD7, 0x8C, 0x78, 0xAF, 0xB1, 0xA5, 0xFC, 0x2D, 0x94, 0xFE, 0x5B, 0x3A, 0xC8, 0x9F, 0x0D,
0xEC, 0xBD, 0x40, 0xDE, 0x0B, 0xFC, 0x8F, 0x07, 0x0B, 0xDC, 0xDD, 0xA5, 0x60, 0x2C, 0x7B, 0x1C,
0xCF, 0xAD, 0x80, 0x29, 0xBD, 0xFD, 0x14, 0x36, 0x28, 0xF1, 0xD4, 0x56, 0x01, 0xA0, 0x2D, 0xFF,
0x36, 0x30, 0x3E, 0x6E, 0x29, 0x3F, 0x4C, 0x14, 0xD4, 0xD6, 0x82, 0xEE, 0x8D, 0x05, 0xA0, 0xFB,
0xA7, 0xFB, 0xDB, 0xDB, 0xEC, 0x00, 0x36, 0xC0, 0x1F, 0x9B, 0x20, 0x68, 0x89, 0xFE, 0xE6, 0xAE,
0xFD, 0xC0, 0xAF, 0x8E, 0xFE, 0x7D, 0xCF, 0xE0, 0x7A, 0xA0, 0xB4, 0xD0, 0xBD, 0x5C, 0xEB, 0x89,
0x01, 0xA0, 0x26, 0x36, 0xB0, 0xF7, 0x96, 0xF5, 0x77, 0xA7, 0x25, 0xFF, 0x36, 0xE0, 0x9F, 0x5B,
0x11, 0x40, 0x02, 0xA0, 0x02, 0xA5, 0xA7, 0xF3, 0x0B, 0x4D, 0xDF, 0xA7, 0x07, 0x9F, 0x37, 0xBA,
0xFF, 0x5A, 0x03, 0x7F, 0x15, 0x5D, 0xFA, 0x1B, 0xD9, 0x18, 0xDF, 0x5A, 0xC9, 0x6A, 0x6A, 0x9B,
0x59, 0x00, 0x36, 0xA5, 0xFD, 0xE3, 0x02, 0x78, 0x0D, 0xFC, 0x37, 0xAF, 0xF3, 0xC8, 0x5F, 0xED,
0x78, 0x36, 0xC6, 0x88, 0x4D, 0x04, 0x78, 0xDB, 0xE5, 0x66, 0xFB, 0xC8, 0xC7, 0x66, 0xBA, 0xBD,
0x44, 0x41, 0x0C, 0xFD, 0xFD, 0xA7, 0x98, 0xF6, 0xCF, 0x9B, 0x4A, 0xD4, 0xFE, 0x1D, 0x5E, 0x22,
0x40, 0x97, 0xFF, 0xE1, 0xB7, 0x5F, 0x9E, 0xBC, 0x0F, 0x35, 0xF3, 0xA6, 0x01, 0xEC, 0xA9, 0xCF,
0x3F, 0x80, 0x34, 0x6A, 0x6D, 0xF9, 0x07, 0x86, 0xD8, 0xD8, 0xCA, 0xAB, 0x14, 0xF0, 0x96, 0xC7,
0x8E, 0xFA, 0x6F, 0x91, 0x00, 0x58, 0x50, 0xE9, 0xC0, 0xDF, 0xBE, 0xCF, 0xD8, 0x44, 0x80, 0x67,
0xAD, 0xD3, 0xFF, 0x21, 0x0D, 0x3B, 0xCA, 0xFF, 0x58, 0xB7, 0x3C, 0x72, 0x94, 0x30, 0xC8, 0x1C,
0xF8, 0xEB, 0x71, 0x3E, 0xB5, 0xB5, 0x63, 0xE9, 0xED, 0xE7, 0x98, 0x9A, 0x08, 0x28, 0x6D, 0x6C,
0xC9, 0x9B, 0x3E, 0xBF, 0xB5, 0xBE, 0x9E, 0x9A, 0x10, 0x38, 0xDE, 0xFF, 0x25, 0xF7, 0x26, 0x1F,
0x0D, 0xF4, 0xF7, 0xCD, 0xF2, 0x9A, 0xE9, 0x77, 0xFB, 0xCB, 0xCB, 0x77, 0x8B, 0x88, 0xC8, 0xDB,
0xCF, 0x7E, 0x75, 0xC9, 0xDD, 0x01, 0x36, 0xBC, 0x7B, 0x3D, 0x2A, 0x00, 0x80, 0x7C, 0xBC, 0x31,
0x01, 0x94, 0x37, 0x18, 0xE0, 0xDC, 0xAE, 0x90, 0x24, 0x00, 0x16, 0x50, 0x5B, 0x1F, 0x7F, 0xAF,
0xA5, 0x5F, 0x79, 0x89, 0x80, 0xB5, 0x06, 0xFE, 0xB9, 0xB3, 0xC3, 0xBD, 0x64, 0x9F, 0x5B, 0xF9,
0x3B, 0x6D, 0x16, 0x75, 0xEC, 0x88, 0xC2, 0x4B, 0x6F, 0x3F, 0x85, 0xFF, 0xDD, 0xC4, 0x95, 0xB5,
0xEC, 0xBD, 0xD5, 0xDB, 0x3E, 0x2D, 0x0D, 0x96, 0xB4, 0x25, 0xDF, 0x9E, 0x3B, 0x43, 0xF4, 0x02,
0x39, 0x65, 0x3E, 0x68, 0x9D, 0xBD, 0x63, 0xCA, 0x71, 0x6C, 0xB7, 0xB1, 0x09, 0x08, 0xEF, 0x35,
0xB5, 0xA5, 0x5F, 0xD7, 0xB7, 0x96, 0xB8, 0x18, 0x72, 0xF2, 0x6F, 0xF5, 0x06, 0xFD, 0xD3, 0xE7,
0x84, 0xD6, 0xB7, 0x22, 0x66, 0x7F, 0x1F, 0xB8, 0xF8, 0x72, 0xF4, 0x73, 0x81, 0x9C, 0x5A, 0x9F,
0x05, 0x40, 0xC5, 0xB6, 0xFC, 0xA7, 0xAA, 0x04, 0x58, 0xEB, 0x3D, 0x30, 0xF2, 0x08, 0x0D, 0xFE,
0xE7, 0x4D, 0xFF, 0x97, 0xBA, 0xF4, 0x5F, 0x91, 0x00, 0x28, 0xA8, 0x54, 0xE0, 0x9F, 0xEB, 0xA6,
0x71, 0xED, 0x27, 0xBB, 0xDC, 0x37, 0xDB, 0x6B, 0xB8, 0x99, 0x8F, 0xD1, 0xCB, 0xDF, 0xD9, 0xA2,
0xDA, 0xBF, 0x1B, 0xDD, 0x3F, 0xEF, 0xC6, 0x2C, 0xB4, 0x5E, 0x03, 0x68, 0xFD, 0x33, 0x43, 0x81,
0x78, 0x6A, 0xB5, 0x7F, 0xBE, 0x25, 0xF5, 0xF8, 0x59, 0xF4, 0xF8, 0x37, 0x03, 0xBD, 0x59, 0xFB,
0xBD, 0x30, 0xF2, 0x08, 0xF5, 0xF9, 0xF7, 0x9E, 0x6F, 0xCD, 0x2D, 0xFD, 0x57, 0x24, 0x00, 0x0A,
0x28, 0xDD, 0xE2, 0x3F, 0xF6, 0xE4, 0xB4, 0xF6, 0x3E, 0xFD, 0xB1, 0x72, 0x67, 0xB9, 0x73, 0x7F,
0xEF, 0xB9, 0xCB, 0xF4, 0x62, 0xF7, 0x9F, 0x8B, 0x63, 0xBD, 0x6A, 0x3F, 0xC6, 0x6B, 0xDF, 0xBF,
0x5E, 0x4D, 0x39, 0xB7, 0x8C, 0xD9, 0x66, 0x2D, 0xDF, 0x4B, 0x4D, 0xC7, 0x6F, 0x68, 0xF0, 0xDF,
0xA5, 0xD7, 0xA3, 0x1E, 0x63, 0x2B, 0xAB, 0x6A, 0x55, 0xAA, 0xE5, 0x3F, 0x54, 0x35, 0x0B, 0x88,
0xF8, 0xD3, 0xFB, 0xE9, 0xB9, 0x50, 0x97, 0x97, 0x9A, 0xD5, 0x88, 0x04, 0x40, 0x46, 0x4B, 0x95,
0xFA, 0xCF, 0x1D, 0x85, 0x1C, 0x69, 0xC5, 0xCE, 0xAA, 0x50, 0xAB, 0xD6, 0xF7, 0x1F, 0xF9, 0xD5,
0x7E, 0x8C, 0xD4, 0xBE, 0x7F, 0xAD, 0x38, 0x79, 0xA3, 0x62, 0x6F, 0xAA, 0xA7, 0xD0, 0xD7, 0xE0,
0x7B, 0xD9, 0x6D, 0xCC, 0xF1, 0xEB, 0x5D, 0xFF, 0x43, 0xF7, 0x05, 0xA5, 0xD6, 0x93, 0x08, 0xA8,
0x47, 0x2B, 0xA5, 0xFD, 0x40, 0x8B, 0x42, 0xA5, 0xFD, 0xB6, 0x85, 0x3F, 0x55, 0xDF, 0xFE, 0x58,
0x24, 0x00, 0x32, 0xA8, 0xAD, 0x8F, 0x3F, 0xC6, 0x49, 0xFD, 0xE3, 0x2B, 0xDD, 0xC7, 0xB3, 0xF5,
0xFD, 0x2F, 0x45, 0x4F, 0xCA, 0x53, 0x6F, 0x82, 0x74, 0xBB, 0xA5, 0xB6, 0x9F, 0xC3, 0x3B, 0x46,
0xBC, 0x96, 0xA0, 0xD0, 0x31, 0x95, 0xFA, 0x18, 0xE1, 0x18, 0x6E, 0x8F, 0xF7, 0x19, 0x7B, 0xAD,
0x6E, 0xA9, 0xCA, 0x18, 0x6B, 0x14, 0x7B, 0xFC, 0xDA, 0x29, 0x67, 0xBD, 0x19, 0x27, 0xE6, 0x1C,
0xBF, 0xB4, 0xFC, 0xC3, 0x5A, 0x6B, 0xE0, 0x5F, 0x7A, 0x0C, 0x80, 0xB5, 0x55, 0x02, 0xE8, 0xDF,
0x13, 0x9A, 0x9F, 0xBE, 0x95, 0xE5, 0xA5, 0xCE, 0x3D, 0xF6, 0x1C, 0x68, 0x97, 0x6D, 0x62, 0x40,
0x8F, 0xC7, 0xD2, 0x09, 0x01, 0x12, 0x00, 0x09, 0xE5, 0x1E, 0xD5, 0x3F, 0xF6, 0x75, 0x63, 0x5B,
0x0B, 0x68, 0xF9, 0x47, 0xCF, 0xE6, 0x1E, 0xFF, 0x73, 0x7F, 0xDF, 0x35, 0xDD, 0x18, 0x87, 0x4A,
0x40, 0x53, 0xB4, 0xF6, 0x62, 0x5D, 0xEC, 0x4D, 0xB4, 0x3B, 0x82, 0xBF, 0x73, 0xEC, 0xF4, 0x9C,
0x94, 0xB1, 0x81, 0xBF, 0x7D, 0x7C, 0xCE, 0xD4, 0x93, 0x2A, 0x74, 0x7E, 0x59, 0x7A, 0x3D, 0xCA,
0x5B, 0x6B, 0xE0, 0x0F, 0x2C, 0xC9, 0x56, 0x37, 0x85, 0xFA, 0xFA, 0xDB, 0x04, 0x85, 0x17, 0xE8,
0xE7, 0x1E, 0x53, 0x86, 0x04, 0x40, 0x02, 0xA5, 0xA7, 0xF3, 0x1B, 0x3B, 0x7D, 0x9F, 0x4D, 0x04,
0x10, 0xF8, 0xC7, 0x3B, 0x38, 0x38, 0x10, 0x11, 0x91, 0x73, 0xE7, 0xCE, 0xCD, 0x7A, 0xBC, 0x16,
0x07, 0xAF, 0xFE, 0x8D, 0x88, 0x88, 0x5C, 0xFE, 0xD4, 0xE1, 0x10, 0xE7, 0x67, 0x1F, 0xFF, 0x98,
0x88, 0xCC, 0x9F, 0xAA, 0x0F, 0x6D, 0x5A, 0x4B, 0xDF, 0x4F, 0x94, 0x67, 0x03, 0xFF, 0x2B, 0x3E,
0x75, 0x78, 0x3B, 0xF1, 0xEA, 0xE3, 0xBF, 0xDF, 0x5A, 0xBE, 0xF0, 0xF8, 0x95, 0xBA, 0x81, 0x88,
0x6C, 0x4F, 0x69, 0xD4, 0x5B, 0x12, 0xC0, 0x0B, 0xFC, 0xD5, 0x83, 0x1F, 0x39, 0xBA, 0xA6, 0x3F,
0x75, 0x78, 0x8D, 0x9F, 0x92, 0x08, 0xF0, 0xEE, 0x0B, 0x42, 0xD7, 0xFD, 0x52, 0xEB, 0xD7, 0xD2,
0x5A, 0xDA, 0x12, 0xFD, 0xCC, 0xD7, 0x9E, 0x94, 0x29, 0xD5, 0xF2, 0xBF, 0x56, 0xB5, 0xB4, 0xDC,
0xA7, 0x5A, 0x2E, 0x75, 0xAE, 0xB1, 0xEF, 0x67, 0xDF, 0x57, 0x97, 0xBD, 0x31, 0x01, 0x74, 0xD9,
0x26, 0x02, 0x72, 0x37, 0xB8, 0x90, 0x00, 0x98, 0xA1, 0x74, 0xE0, 0x6F, 0xDF, 0x67, 0x6C, 0x22,
0xC0, 0xC3, 0x05, 0xD9, 0xA7, 0x81, 0x7C, 0x28, 0xB0, 0x6F, 0x25, 0xF0, 0x0F, 0xAD, 0xEF, 0x31,
0x11, 0x10, 0x9B, 0x10, 0x9B, 0x9B, 0x8D, 0x6D, 0x71, 0x84, 0x70, 0x7B, 0xA1, 0xAA, 0xB1, 0x02,
0xC0, 0x9E, 0x87, 0x97, 0x1A, 0x50, 0xA7, 0x57, 0x5E, 0xE0, 0xAF, 0xCB, 0x08, 0xD3, 0xC0, 0x5F,
0x8F, 0xDD, 0x50, 0xA2, 0x60, 0x8C, 0x5A, 0x02, 0x7F, 0x00, 0x58, 0x23, 0x1B, 0x43, 0x79, 0xB1,
0x59, 0xEC, 0x98, 0x00, 0xA5, 0xC6, 0x02, 0x20, 0x01, 0x30, 0x41, 0x6D, 0x7D, 0xFC, 0x43, 0x99,
0x75, 0x2F, 0x11, 0x40, 0xE0, 0x1F, 0xCF, 0x26, 0x02, 0x54, 0xEB, 0x81, 0xBF, 0xF7, 0xFC, 0x9E,
0x12, 0x01, 0xFC, 0x0E, 0xDA, 0x62, 0xCF, 0xBF, 0xDF, 0xFA, 0xEC, 0x3B, 0x07, 0xD7, 0x93, 0x08,
0xC8, 0xC3, 0xB6, 0xFC, 0x6B, 0xA0, 0xFF, 0xD8, 0xE5, 0x57, 0x0E, 0x1F, 0x3F, 0x6A, 0xF1, 0xDF,
0x2C, 0x1F, 0x3D, 0x6F, 0xD3, 0x45, 0x80, 0xEE, 0x24, 0x1B, 0x36, 0xF0, 0xB7, 0xC7, 0xF6, 0x43,
0x32, 0xFE, 0x18, 0x5E, 0x3A, 0xA0, 0x27, 0xE0, 0xC7, 0x52, 0x4A, 0x8F, 0x01, 0xB0, 0x36, 0x5E,
0x1F, 0x7A, 0x5B, 0x41, 0x52, 0xEB, 0xF2, 0xD2, 0x63, 0x00, 0x84, 0x46, 0xFD, 0xB7, 0x95, 0x00,
0x7A, 0xAE, 0xD4, 0xE7, 0x97, 0xBE, 0x26, 0x92, 0x00, 0x18, 0xA1, 0x54, 0xE0, 0x9F, 0xAB, 0xA5,
0x90, 0x40, 0x67, 0x3A, 0x1B, 0xF8, 0x7B, 0xEB, 0x6B, 0x4D, 0x04, 0xC0, 0x17, 0x7B, 0xC3, 0x1A,
0x5B, 0x51, 0x93, 0x6B, 0xFB, 0x1C, 0xD3, 0x3C, 0x6A, 0xE0, 0xA1, 0x37, 0x42, 0xB6, 0x8F, 0xE8,
0xD2, 0xD3, 0xD4, 0xEC, 0xA2, 0x81, 0xFF, 0xA7, 0xBF, 0xF9, 0xF3, 0xAD, 0x65, 0x94, 0xA5, 0x81,
0xFE, 0x17, 0xEE, 0xB9, 0x5A, 0x44, 0x8E, 0x13, 0x02, 0xBA, 0xFC, 0xC0, 0xC5, 0x97, 0x45, 0x64,
0xC7, 0x18, 0x01, 0xD8, 0xF0, 0x12, 0x01, 0x63, 0xC4, 0x26, 0xFC, 0x97, 0x5E, 0x0F, 0x00, 0x6B,
0x12, 0x6A, 0xE1, 0xB7, 0x63, 0x04, 0x78, 0x01, 0x7F, 0xA9, 0x44, 0x00, 0x09, 0x80, 0x08, 0xA5,
0x5B, 0xFC, 0xC7, 0x5E, 0x28, 0xC9, 0xB8, 0xE7, 0x13, 0x0A, 0xFC, 0x6B, 0xA5, 0x2D, 0xF9, 0x37,
0xFC, 0xF3, 0xDB, 0x44, 0x44, 0xE4, 0xE9, 0xFF, 0xF7, 0xF5, 0xAD, 0xF5, 0xDA, 0xF7, 0xDF, 0xD2,
0xE7, 0x1F, 0xBC, 0x7A, 0xB8, 0xDC, 0x53, 0x25, 0x40, 0xCF, 0x6C, 0xA0, 0xAF, 0xE7, 0xBC, 0x9A,
0xBA, 0x2D, 0xE8, 0x3E, 0xD9, 0xC0, 0x1F, 0xE5, 0xEC, 0xED, 0xED, 0x6D, 0x02, 0x79, 0x1B, 0xF8,
0x2B, 0xED, 0x02, 0xA0, 0xDC, 0x44, 0x00, 0xAD, 0x6F, 0x72, 0xDD, 0xFB, 0xFE, 0x56, 0x44, 0x4E,
0x57, 0xAD, 0xDC, 0xFE, 0x83, 0xF9, 0xC9, 0x3E, 0x9B, 0xCC, 0xB3, 0xF7, 0x2D, 0xA1, 0xFB, 0x8C,
0xB9, 0xEB, 0x43, 0xEF, 0x0F, 0xA4, 0x46, 0xCB, 0xFF, 0x3C, 0x4B, 0xF5, 0xA1, 0x4F, 0x65, 0xE9,
0xFD, 0xB7, 0xEF, 0x67, 0x03, 0x7F, 0xDB, 0x88, 0xB3, 0x74, 0x15, 0x1C, 0x09, 0x80, 0x1D, 0x96,
0x2A, 0xF5, 0x1F, 0xDB, 0xD2, 0x47, 0x02, 0xA0, 0x3C, 0xEF, 0x3B, 0xAA, 0xA5, 0xFC, 0x58, 0x03,
0xF9, 0xF9, 0xDB, 0xAF, 0x3F, 0x01, 0x60, 0x4F, 0xC2, 0x9A, 0xF4, 0x89, 0xFD, 0x5D, 0x2D, 0xBD,
0xFD, 0x1C, 0xA1, 0x96, 0xC6, 0x9A, 0xFB, 0xFE, 0x93, 0x08, 0xA8, 0x8B, 0x06, 0xF8, 0x36, 0xE0,
0xB7, 0x09, 0x82, 0x9E, 0xE9, 0xA0, 0x7E, 0x29, 0xFB, 0xF8, 0x5B, 0xDE, 0xB5, 0xC9, 0xF6, 0x4B,
0xCD, 0xD5, 0xC2, 0x1F, 0xFB, 0xFE, 0x00, 0xB0, 0x66, 0xA1, 0xC1, 0x0A, 0x6D, 0xE5, 0x70, 0xE9,
0xF8, 0x81, 0x04, 0xC0, 0x80, 0xDA, 0xFA, 0xF8, 0xA3, 0x3C, 0xAF, 0xE5, 0x5F, 0x6F, 0x62, 0x6C,
0x4B, 0xA9, 0x5D, 0xAE, 0x25, 0x11, 0x00, 0xEC, 0x62, 0x2F, 0x48, 0x2D, 0x1D, 0xB7, 0xB6, 0xF4,
0x9F, 0xAE, 0x00, 0x68, 0xC5, 0xDF, 0xFF, 0xA7, 0x7F, 0xBB, 0xB5, 0x9C, 0xF3, 0xF7, 0x66, 0xEF,
0x23, 0x34, 0x70, 0xB7, 0x49, 0x3D, 0x7B, 0x93, 0x3A, 0x75, 0x7D, 0xE8, 0xFD, 0x81, 0x5C, 0x18,
0x03, 0x60, 0x9E, 0xA5, 0xFB, 0xD0, 0xCF, 0x55, 0xCB, 0xFE, 0xDB, 0x24, 0x68, 0xEC, 0x2C, 0x05,
0xB6, 0xE2, 0x32, 0xF7, 0xFD, 0x18, 0x09, 0x80, 0x13, 0x72, 0x8F, 0xEA, 0x1F, 0xFB, 0xBA, 0xB1,
0xE5, 0x2B, 0xB4, 0xFC, 0x2F, 0xCF, 0xFE, 0x30, 0x53, 0xF4, 0xE1, 0x4C, 0xC1, 0x96, 0xFC, 0xE7,
0xDE, 0x0E, 0x6D, 0xF2, 0x6E, 0xDA, 0xED, 0xF1, 0x6B, 0x2F, 0x60, 0xAA, 0x44, 0xA2, 0x80, 0xD2,
0xFF, 0x3A, 0x69, 0x29, 0x7F, 0xA8, 0xE5, 0x9F, 0x31, 0x00, 0xE2, 0x7D, 0xE8, 0xBD, 0xDF, 0x15,
0x11, 0x91, 0x07, 0x3F, 0xF2, 0x09, 0x11, 0x99, 0x36, 0x08, 0xA0, 0x0A, 0xDD, 0x67, 0x84, 0xAE,
0x51, 0x73, 0xD7, 0xB7, 0x16, 0x3C, 0x00, 0xC0, 0x14, 0xB6, 0xBA, 0xC9, 0x9B, 0x16, 0xD0, 0x3E,
0xAE, 0xDB, 0x79, 0x81, 0x7E, 0xEE, 0x2E, 0x98, 0x24, 0x00, 0xA4, 0xFC, 0x74, 0x7E, 0x63, 0xA7,
0xEF, 0x0B, 0x95, 0xDE, 0x21, 0xBD, 0xCB, 0x8F, 0x3C, 0xB8, 0xB5, 0x7C, 0xF6, 0x33, 0xB7, 0x6F,
0x2D, 0x2F, 0x1D, 0xE0, 0x87, 0x68, 0xDF, 0x7D, 0x3B, 0x0B, 0x80, 0x7D, 0x3C, 0xF4, 0x3C, 0xAC,
0x9B, 0x17, 0xD8, 0x8F, 0x6D, 0xE5, 0x2B, 0x29, 0x94, 0x08, 0xB8, 0xE5, 0x91, 0x5F, 0x94, 0xDC,
0x9D, 0x6E, 0x69, 0x5F, 0x7F, 0x1D, 0xF5, 0xDF, 0x06, 0xFA, 0x5E, 0xE0, 0xAF, 0x63, 0x07, 0xF4,
0x48, 0x03, 0x7C, 0xF5, 0xC3, 0x97, 0x3E, 0x39, 0xF8, 0xF8, 0x1C, 0xDE, 0x7D, 0x85, 0xBD, 0x6F,
0xB0, 0x37, 0x97, 0xDE, 0x39, 0x60, 0xEC, 0xFA, 0xD0, 0xFB, 0xB7, 0xD6, 0xAF, 0x18, 0xED, 0xA0,
0xE5, 0x7F, 0x9E, 0xA5, 0xFB, 0xD0, 0xCF, 0xB5, 0xD4, 0xFE, 0x7B, 0x01, 0xBF, 0x35, 0xB6, 0xC5,
0x3F, 0x77, 0xD7, 0xCB, 0xAE, 0x13, 0x00, 0xA5, 0x03, 0x7F, 0xFB, 0x3E, 0x63, 0x13, 0x01, 0x9E,
0xD6, 0x7E, 0xA4, 0x2D, 0xD2, 0x84, 0xC0, 0x19, 0xD3, 0x57, 0xC7, 0x53, 0x4B, 0x09, 0xB5, 0x0D,
0xF0, 0x75, 0xBF, 0xB5, 0xA5, 0xDF, 0x2E, 0x13, 0xF8, 0xF7, 0x25, 0x74, 0x53, 0x6F, 0xD5, 0x72,
0x5C, 0x8B, 0x50, 0xFA, 0xBF, 0x34, 0x1D, 0xED, 0xDF, 0x26, 0x02, 0x14, 0x2D, 0xFE, 0xD3, 0xA5,
0x18, 0x04, 0x50, 0x79, 0xF7, 0x0F, 0xA1, 0xE4, 0xDE, 0xDC, 0xF5, 0xA1, 0xF7, 0x07, 0x80, 0x35,
0xB0, 0x31, 0x98, 0x8D, 0xED, 0xBC, 0x65, 0xFB, 0x7C, 0xBD, 0xFF, 0x2A, 0xD5, 0x15, 0xB3, 0xCB,
0x04, 0x40, 0x6D, 0x7D, 0xFC, 0x43, 0x99, 0xF1, 0xD8, 0x69, 0x7D, 0x90, 0x9F, 0x8E, 0x0D, 0x10,
0xFA, 0x61, 0xD6, 0x32, 0x2D, 0xA0, 0x6D, 0xD9, 0xD7, 0xFD, 0xD6, 0xC7, 0xED, 0xB2, 0xAD, 0x0C,
0xE8, 0x81, 0xB6, 0x06, 0x8C, 0xFD, 0x3D, 0x85, 0xB2, 0xBD, 0xB9, 0xB7, 0x4F, 0xC1, 0x56, 0x00,
0x78, 0xD3, 0x00, 0x5A, 0x35, 0x24, 0x02, 0xE8, 0x0A, 0xB0, 0x2C, 0x0D, 0xFC, 0x43, 0x25, 0xFE,
0xDA, 0xE2, 0x4F, 0x42, 0xE0, 0x74, 0x8B, 0xBF, 0xFE, 0xAB, 0x8F, 0xAB, 0xB7, 0xC9, 0xF4, 0x41,
0x02, 0x63, 0x1B, 0x0C, 0xBC, 0x51, 0xFA, 0xE7, 0xAE, 0x27, 0xE0, 0xC7, 0x52, 0x6C, 0xCB, 0x7E,
0x68, 0x19, 0xDB, 0x6A, 0xE9, 0x43, 0x3F, 0xD5, 0xD2, 0xFB, 0x6F, 0x03, 0x79, 0xAF, 0x22, 0xC1,
0x5B, 0x5F, 0x7A, 0xB0, 0xE5, 0xAE, 0x12, 0x00, 0xA5, 0x02, 0xFF, 0x5C, 0xFD, 0x36, 0x08, 0xF8,
0xCB, 0xD1, 0x92, 0x7F, 0xDB, 0x15, 0x40, 0xD5, 0x3E, 0x3D, 0xA0, 0x0D, 0xFC, 0xA7, 0x6E, 0xBF,
0xA6, 0x44, 0x40, 0xA8, 0xFC, 0x2F, 0xD4, 0x52, 0x66, 0xB7, 0x1F, 0x3B, 0xCA, 0xFF, 0xDC, 0xED,
0x73, 0x94, 0xDF, 0xDB, 0x0B, 0x90, 0xF7, 0xDE, 0x4B, 0x76, 0x79, 0xF1, 0x4A, 0xFE, 0x6D, 0x8B,
0x3F, 0xA5, 0xFF, 0xF9, 0xED, 0xEF, 0xEF, 0x6F, 0xA6, 0xEF, 0xD3, 0x16, 0x7F, 0x3B, 0x2D, 0xA0,
0xE5, 0xAD, 0xDF, 0xDF, 0xDF, 0xE7, 0x86, 0xFC, 0x48, 0xCA, 0x16, 0x9F, 0xD8, 0x06, 0x83, 0xD0,
0xFD, 0xCF, 0xD4, 0xF5, 0x34, 0x58, 0xA0, 0xB4, 0x9A, 0xA6, 0xAB, 0x45, 0xBF, 0xBC, 0x40, 0xDF,
0x3B, 0xF7, 0x79, 0x81, 0x7F, 0xA9, 0x44, 0x40, 0x57, 0x09, 0x00, 0x95, 0x3B, 0x2B, 0x34, 0xF6,
0x42, 0x47, 0xC6, 0xBC, 0x3E, 0xDA, 0x72, 0x3F, 0x35, 0xD0, 0x5F, 0xBA, 0xE5, 0xDF, 0x4E, 0x03,
0xA8, 0x25, 0xFE, 0x36, 0x31, 0x60, 0x97, 0x4F, 0x4F, 0x1F, 0xB8, 0x9E, 0x04, 0x80, 0x9A, 0x7B,
0x72, 0x5D, 0x7A, 0xFB, 0x94, 0x42, 0x83, 0x56, 0xEA, 0xBE, 0x2E, 0xD9, 0xE2, 0x7F, 0x1C, 0xD8,
0x0F, 0x97, 0xF8, 0x13, 0xF8, 0x2F, 0xCB, 0xEB, 0x02, 0x80, 0xD3, 0x6C, 0x8B, 0x7F, 0xCA, 0xBE,
0xFF, 0x96, 0xD7, 0x42, 0xAF, 0x42, 0x83, 0x13, 0xCF, 0x5D, 0x1F, 0x7A, 0xFF, 0xB1, 0x6C, 0xB2,
0xA8, 0x86, 0xF1, 0x48, 0x50, 0x87, 0xD8, 0xEE, 0xB2, 0xD8, 0x8D, 0x31, 0x00, 0xC6, 0xF1, 0x5A,
0xFC, 0xF5, 0x5C, 0xA7, 0xCB, 0x35, 0x54, 0x4C, 0x0E, 0xE9, 0x32, 0x01, 0x90, 0x9B, 0x37, 0x0F,
0xAE, 0x87, 0x93, 0x56, 0xBD, 0xC6, 0x26, 0x02, 0xC6, 0x7E, 0xF7, 0x40, 0x0D, 0xEC, 0x05, 0xAC,
0xC6, 0x0B, 0x16, 0x81, 0x7E, 0x5D, 0x36, 0xA5, 0xFD, 0x81, 0xC0, 0xBF, 0xE7, 0x41, 0xFF, 0x3C,
0x36, 0x11, 0x70, 0xDD, 0xFB, 0xD2, 0x25, 0x02, 0xBC, 0x6B, 0x50, 0xE8, 0xDA, 0x54, 0x6A, 0x7D,
0xAA, 0x06, 0x18, 0x6F, 0x00, 0xD3, 0xA9, 0xCF, 0xCB, 0xB1, 0xFD, 0x92, 0xEF, 0xDD, 0x23, 0x12,
0x01, 0x28, 0x29, 0xB6, 0xB4, 0x5F, 0xD5, 0x36, 0xCD, 0x32, 0x09, 0x80, 0x8C, 0x5A, 0xEB, 0x3F,
0x03, 0x9F, 0xDE, 0xBC, 0xD8, 0x1F, 0x6E, 0xED, 0xB3, 0x01, 0x00, 0xBB, 0xD8, 0x0B, 0x92, 0x5E,
0xB8, 0x6A, 0xBB, 0x50, 0xA1, 0x3E, 0xB6, 0xB4, 0x3F, 0xD4, 0x15, 0x00, 0xA7, 0xE5, 0xAC, 0x00,
0x50, 0xA5, 0x5B, 0xFE, 0xC7, 0x4E, 0x7B, 0x3C, 0xD7, 0xD8, 0xD7, 0x9F, 0xBB, 0x3F, 0x73, 0xB6,
0xE7, 0x9E, 0xB0, 0x0C, 0x12, 0x01, 0xD3, 0xA4, 0xEA, 0x43, 0x3F, 0xB7, 0x21, 0x6C, 0xEA, 0xFB,
0x96, 0x1A, 0x03, 0xC0, 0x1B, 0xC4, 0xCF, 0xEE, 0x87, 0x1E, 0x87, 0x5A, 0x49, 0x59, 0x5B, 0x42,
0x80, 0x04, 0x40, 0x84, 0xB1, 0x17, 0xB4, 0xD8, 0xF2, 0x13, 0x4E, 0x4E, 0xF5, 0xB3, 0x01, 0xBE,
0xFD, 0xC1, 0xD6, 0x1A, 0x20, 0x79, 0xA5, 0xDD, 0x5E, 0x9F, 0x7E, 0x6F, 0xB0, 0x40, 0xF4, 0xC1,
0x1E, 0xD7, 0x24, 0xB6, 0x10, 0x4B, 0x03, 0x7F, 0xDB, 0x15, 0xC0, 0x4B, 0x04, 0xD4, 0xD4, 0x05,
0xA6, 0x94, 0xA9, 0x81, 0xFE, 0xE7, 0x9F, 0x3A, 0x1C, 0x0C, 0xF0, 0xB5, 0x27, 0xC7, 0x6F, 0x9B,
0xAB, 0x8F, 0x7F, 0xAA, 0xF5, 0x48, 0x27, 0x54, 0x5D, 0xB1, 0xF6, 0xF5, 0x1E, 0x12, 0x01, 0x65,
0xD9, 0x92, 0xF8, 0xB9, 0x6A, 0xE9, 0x82, 0x60, 0x8F, 0xBF, 0x50, 0x5F, 0x7F, 0x9B, 0x80, 0xF0,
0x02, 0xFD, 0xA5, 0xC7, 0xAE, 0x20, 0x01, 0xB0, 0x83, 0x77, 0xD2, 0x19, 0x3B, 0x7D, 0x9F, 0x3D,
0x88, 0x39, 0x19, 0xB5, 0xA3, 0xF5, 0x40, 0x38, 0x76, 0xFF, 0x8F, 0x13, 0x03, 0xEB, 0xEB, 0xF3,
0x0F, 0x9F, 0x1E, 0x1F, 0x0C, 0xC6, 0x86, 0x58, 0x9B, 0x00, 0x5E, 0x07, 0x03, 0x3C, 0x4A, 0x00,
0xE8, 0xB4, 0x80, 0x8A, 0x8A, 0x80, 0xF1, 0x81, 0xBF, 0x06, 0xFC, 0x73, 0x78, 0xF7, 0x25, 0xA1,
0xFB, 0x8E, 0x52, 0xEB, 0x6B, 0xB9, 0xA9, 0x5F, 0x93, 0xD0, 0x8C, 0x52, 0x6B, 0x5F, 0x1F, 0x42,
0x22, 0x20, 0xCE, 0xD4, 0x3E, 0xF4, 0x5E, 0xE0, 0x1F, 0xBB, 0xBD, 0x7E, 0x2F, 0xF6, 0xFD, 0xC7,
0x7E, 0xDF, 0xB9, 0xC6, 0x00, 0xB0, 0xAF, 0x67, 0x5F, 0x57, 0x97, 0x43, 0xB3, 0x00, 0xD8, 0x44,
0xC0, 0xD2, 0xC9, 0x70, 0x12, 0x00, 0x03, 0x42, 0x01, 0xBE, 0x37, 0xB7, 0xA3, 0x15, 0x7B, 0xD2,
0xE1, 0x82, 0x08, 0x60, 0x49, 0xDE, 0x85, 0xC8, 0x56, 0x02, 0xB4, 0x9E, 0x10, 0x43, 0x3A, 0x9B,
0xD9, 0x2D, 0x16, 0xDD, 0x8B, 0x3A, 0xA5, 0x2A, 0xED, 0x7F, 0xE8, 0xA3, 0xCF, 0x88, 0xC8, 0xBC,
0x04, 0x5D, 0x2D, 0x81, 0x3F, 0xF2, 0xB1, 0x9F, 0xB1, 0x0D, 0x9C, 0xD6, 0xBE, 0x3E, 0x16, 0x89,
0x80, 0x32, 0xE6, 0x06, 0xFE, 0xA1, 0xD9, 0x89, 0x4A, 0xB3, 0x7F, 0x8F, 0x17, 0xFB, 0xC5, 0x8E,
0x09, 0xB0, 0x74, 0xE9, 0xBF, 0x22, 0x01, 0x30, 0x43, 0x6C, 0x56, 0x32, 0xF7, 0xB4, 0x38, 0xB5,
0x9D, 0xCC, 0x5A, 0x4F, 0x68, 0xB4, 0xDE, 0x1A, 0xDA, 0xFA, 0xFE, 0x23, 0xBF, 0xD0, 0x31, 0xE2,
0x5D, 0x78, 0x4B, 0x1D, 0x5B, 0x1C, 0xC3, 0x75, 0xE2, 0x7B, 0x89, 0x73, 0xE5, 0x5B, 0x9F, 0x9D,
0xB4, 0xDD, 0x6B, 0x4F, 0xFE, 0xE9, 0xD6, 0xF2, 0x94, 0xCF, 0x7B, 0xE9, 0x80, 0xBE, 0xB6, 0xFB,
0x91, 0x1E, 0xD4, 0x12, 0x88, 0x2F, 0xBD, 0x3E, 0x56, 0xAA, 0x44, 0x40, 0xEB, 0xF7, 0xBA, 0x56,
0xA9, 0x3E, 0xF4, 0x5E, 0xE0, 0x3F, 0xF7, 0xF3, 0xCC, 0xBD, 0xFF, 0xA1, 0x51, 0xFF, 0x6D, 0x25,
0x80, 0xFE, 0x9D, 0xDE, 0x74, 0x7F, 0x4B, 0x4B, 0x92, 0x00, 0x18, 0xDB, 0x5F, 0x74, 0xE9, 0xAC,
0x47, 0x48, 0xAE, 0xAC, 0xD3, 0xDA, 0x4E, 0x16, 0x56, 0xEE, 0x93, 0x46, 0x6E, 0xB5, 0x64, 0x1B,
0xA7, 0x6A, 0x7D, 0xFF, 0x91, 0x5F, 0xED, 0xC7, 0x48, 0xED, 0xFB, 0xD7, 0x2B, 0xBE, 0x97, 0x38,
0x35, 0x7C, 0x4E, 0xB1, 0x0D, 0x0E, 0x4B, 0xAF, 0x47, 0x7A, 0xA1, 0xC0, 0x76, 0xED, 0xEB, 0xC7,
0x9A, 0xFA, 0x7A, 0x1C, 0xCB, 0xD3, 0xE4, 0x0A, 0xFC, 0x4B, 0x09, 0xB5, 0xF0, 0xDB, 0x31, 0x02,
0xBC, 0x80, 0xBF, 0x96, 0x44, 0xC0, 0xAC, 0x04, 0x80, 0x0D, 0xFC, 0xFF, 0xFE, 0xD7, 0x7F, 0xBD,
0xF3, 0xF9, 0x6F, 0xBB, 0xE6, 0x2B, 0x83, 0xDB, 0x59, 0x4B, 0x27, 0x08, 0xC6, 0x1E, 0x8C, 0xB5,
0x64, 0xBC, 0xF5, 0xA0, 0xB2, 0xAD, 0x06, 0xF6, 0x60, 0xCB, 0xB5, 0xBE, 0xF5, 0xA9, 0x6A, 0x5A,
0xDF, 0xFF, 0x5A, 0x94, 0x9E, 0x0A, 0xB1, 0xD5, 0x84, 0xD3, 0x12, 0x72, 0x1F, 0xE3, 0x73, 0xBF,
0x8B, 0x9C, 0x37, 0x02, 0x25, 0x8E, 0xCB, 0x16, 0x8F, 0xC5, 0x98, 0xCF, 0x65, 0xCE, 0x67, 0xD7,
0xE2, 0x67, 0x32, 0x55, 0x4D, 0xBF, 0xAF, 0xD0, 0x6F, 0x69, 0xE9, 0xF5, 0xC8, 0x67, 0xE9, 0xEF,
0x76, 0xE9, 0xF5, 0x63, 0x71, 0xAC, 0x1E, 0xCA, 0x15, 0x98, 0x97, 0x0A, 0xFC, 0x73, 0xBF, 0xBE,
0xD7, 0xF7, 0xDF, 0x9E, 0x97, 0xBD, 0xC1, 0x00, 0x6D, 0xD7, 0x01, 0x9D, 0x66, 0x7C, 0x29, 0x49,
0x2A, 0x00, 0x34, 0xF0, 0xD7, 0x00, 0x3F, 0xF6, 0x79, 0xA1, 0x84, 0xC1, 0x52, 0xC6, 0xDE, 0xEC,
0xD4, 0x92, 0x00, 0x40, 0x9D, 0x72, 0xCF, 0xE5, 0xDB, 0x5A, 0x16, 0x35, 0x95, 0x5E, 0xFF, 0xEE,
0x1A, 0xB5, 0xF0, 0x5D, 0x5C, 0xB8, 0x70, 0x81, 0xDF, 0x60, 0x41, 0x7C, 0x26, 0xE9, 0xF0, 0x59,
0x02, 0x68, 0x4D, 0xEB, 0x2D, 0xFE, 0x73, 0x79, 0x63, 0x00, 0xE8, 0xF2, 0xC1, 0xC1, 0x81, 0x88,
0x1C, 0x27, 0x02, 0x4A, 0x8F, 0x0D, 0x30, 0x29, 0x01, 0x10, 0x5B, 0xF2, 0xEF, 0x05, 0xF8, 0x36,
0x11, 0xB0, 0x74, 0x8B, 0xBF, 0xA7, 0xB5, 0x96, 0x0B, 0x6D, 0x99, 0xF7, 0xB2, 0x54, 0x9E, 0xDA,
0xB2, 0xB1, 0x4B, 0x4B, 0x7D, 0x3C, 0xDA, 0x8A, 0x89, 0xDC, 0xAF, 0x5F, 0x8B, 0xDC, 0xBF, 0xEB,
0x5A, 0xFF, 0xEE, 0x16, 0x84, 0xBE, 0x9B, 0x87, 0x3F, 0x36, 0xDC, 0x87, 0xF9, 0x73, 0xDF, 0xBF,
0x6E, 0x70, 0xFB, 0x94, 0xDF, 0x45, 0xEA, 0x84, 0xEA, 0xD0, 0xF9, 0x29, 0xF7, 0x6F, 0xB0, 0xC4,
0xDF, 0x90, 0x5A, 0x8E, 0xDF, 0x6B, 0xAF, 0xBF, 0xD1, 0x5E, 0xCE, 0xF1, 0x00, 0xD6, 0x23, 0x55,
0x1F, 0xFA, 0xA5, 0x02, 0xFF, 0x52, 0x63, 0x18, 0x84, 0xD8, 0x46, 0x64, 0x2F, 0x11, 0x60, 0x3F,
0x0F, 0xDB, 0x85, 0x2C, 0x77, 0x42, 0x60, 0x56, 0x05, 0x80, 0xD7, 0xF2, 0x1F, 0x5B, 0x11, 0xA0,
0x6A, 0x19, 0x11, 0x51, 0xC5, 0x1E, 0xAC, 0xB5, 0xB6, 0xFC, 0x2F, 0xB5, 0x5F, 0xA5, 0x4B, 0xBF,
0x01, 0xCC, 0x63, 0xCF, 0xBD, 0x1A, 0xF8, 0x6B, 0xA0, 0xFF, 0x8D, 0x1B, 0x9F, 0xDB, 0x7A, 0xBE,
0x5D, 0xAE, 0xD9, 0xD2, 0x37, 0x01, 0x29, 0x2C, 0xF1, 0x37, 0xCC, 0xED, 0x22, 0x06, 0x00, 0xE8,
0xD7, 0xDC, 0xE9, 0x04, 0x5B, 0x63, 0x4B, 0xFB, 0xBD, 0x69, 0x03, 0xED, 0xE3, 0xBA, 0x9D, 0x17,
0x03, 0xE7, 0x1E, 0x53, 0x26, 0xE9, 0x2C, 0x00, 0x5E, 0xE0, 0xEF, 0x05, 0xF6, 0x63, 0x07, 0x0F,
0x2C, 0xC5, 0x0E, 0x0C, 0x12, 0x1A, 0xDC, 0xA6, 0x16, 0x8C, 0x01, 0x90, 0x47, 0xAE, 0x41, 0x2E,
0xED, 0x8F, 0xDE, 0x7B, 0x9F, 0x5A, 0x12, 0x63, 0xE8, 0x8F, 0x97, 0x08, 0x28, 0x91, 0xB4, 0xF5,
0x6E, 0x1A, 0x5A, 0x1A, 0x5C, 0xAC, 0xE6, 0x7D, 0x03, 0x00, 0x40, 0x4D, 0x0D, 0xDC, 0xBD, 0xE9,
0xF0, 0xA6, 0xC6, 0x04, 0x53, 0x2B, 0x06, 0x96, 0xEA, 0x6A, 0xE0, 0x05, 0xFC, 0xD6, 0xD8, 0x16,
0xFF, 0xDC, 0xC9, 0xF5, 0xAC, 0xD3, 0x00, 0x86, 0x02, 0xFF, 0x50, 0xE0, 0xB3, 0xB4, 0xD8, 0x11,
0x42, 0x6B, 0xBB, 0xC9, 0x6B, 0x6D, 0x24, 0xD6, 0x5A, 0x4D, 0x3D, 0x2E, 0xC7, 0x06, 0x47, 0xA1,
0xF7, 0xA9, 0xAD, 0x42, 0x06, 0xED, 0xB3, 0xC7, 0xDC, 0xA9, 0x63, 0xEC, 0xC6, 0xED, 0x4A, 0x00,
0xE5, 0x25, 0x04, 0xD0, 0xBE, 0x50, 0x99, 0xB9, 0xB7, 0x5E, 0x1F, 0xA7, 0x12, 0xC0, 0xF7, 0xA1,
0xF7, 0x7E, 0x57, 0x44, 0x44, 0xAE, 0xFD, 0xC0, 0x7B, 0x44, 0x44, 0xE4, 0x85, 0xE7, 0x7F, 0xB2,
0xB5, 0xFE, 0x87, 0x2F, 0x7D, 0xB2, 0xF8, 0x3E, 0x01, 0x40, 0x4A, 0x36, 0x36, 0x98, 0xDA, 0x82,
0x5D, 0x5B, 0x4C, 0x15, 0x62, 0xF7, 0xD7, 0x56, 0x04, 0x78, 0xCB, 0xF6, 0xF9, 0xFA, 0x79, 0x95,
0xBA, 0xE7, 0x4F, 0x3A, 0x08, 0xA0, 0x35, 0xB6, 0x45, 0xF3, 0x97, 0x97, 0xEF, 0x16, 0x11, 0x91,
0xB7, 0x9F, 0xFD, 0x6A, 0x8A, 0xDD, 0x4A, 0x26, 0x76, 0x5A, 0x9D, 0xA5, 0x85, 0x6E, 0xD0, 0xBC,
0xFD, 0x4D, 0xB5, 0x7E, 0x29, 0xAD, 0x4E, 0xE1, 0x32, 0x36, 0x01, 0x46, 0x22, 0x00, 0xB9, 0xE9,
0x31, 0xA6, 0x01, 0x7E, 0xCB, 0x5D, 0x00, 0xC6, 0xFA, 0xF1, 0xC5, 0x2F, 0x8A, 0x88, 0xC8, 0xFB,
0xEF, 0xF9, 0xDA, 0xE0, 0x72, 0xAF, 0xF4, 0xFC, 0x7E, 0xE1, 0xEC, 0x95, 0x22, 0x22, 0x72, 0xC5,
0xA7, 0x0E, 0x6F, 0x1B, 0x5E, 0x7D, 0xFC, 0xF7, 0x22, 0x22, 0xF2, 0xD8, 0xE5, 0x57, 0x16, 0xD9,
0xAF, 0x16, 0xD8, 0xC0, 0xFF, 0x8E, 0x7B, 0xCF, 0x1E, 0xAE, 0xB8, 0xEB, 0xF0, 0x9F, 0x3B, 0xEF,
0x7B, 0xCB, 0xD6, 0xF3, 0x48, 0x04, 0x00, 0x58, 0x4A, 0xAA, 0x3E, 0xF4, 0x4B, 0xDD, 0x63, 0x2F,
0x3D, 0x06, 0x80, 0x0D, 0xE4, 0xBD, 0x8A, 0x04, 0x6F, 0x7D, 0xE9, 0x24, 0xFA, 0xAC, 0x04, 0x40,
0xA8, 0x8F, 0xBF, 0x0D, 0x54, 0x42, 0x81, 0xCE, 0xF1, 0xEB, 0xD5, 0x95, 0x00, 0x50, 0xB5, 0x04,
0x8E, 0x53, 0x79, 0x5D, 0x1A, 0x52, 0xAD, 0xAF, 0x5D, 0xAD, 0x7D, 0x82, 0x6B, 0xAF, 0x84, 0xC1,
0xFA, 0x84, 0xAA, 0xB3, 0x7A, 0x6C, 0xE9, 0x0F, 0x05, 0xFA, 0xBD, 0x26, 0x04, 0x34, 0xF0, 0x57,
0x0F, 0x5C, 0x7C, 0x79, 0x78, 0x3D, 0x95, 0x00, 0xAE, 0x4D, 0x8B, 0xBF, 0x09, 0xFC, 0x95, 0x06,
0xFE, 0x24, 0x02, 0x00, 0xA0, 0x4D, 0x5E, 0xA0, 0xEF, 0xC5, 0x4C, 0x5E, 0xE0, 0x5F, 0xEA, 0x1A,
0x9A, 0x64, 0x10, 0x40, 0x2B, 0x94, 0x18, 0xF0, 0x02, 0x9D, 0x5A, 0xA7, 0x05, 0x6C, 0x85, 0x1E,
0x34, 0xF6, 0x60, 0xB3, 0x07, 0x53, 0xAE, 0xF5, 0x4B, 0x8F, 0x01, 0xF0, 0xEE, 0xFF, 0xFB, 0xEB,
0x45, 0xDF, 0x7F, 0xAA, 0xB1, 0x95, 0x32, 0x54, 0x02, 0x20, 0x15, 0x7B, 0xEC, 0xD9, 0xC1, 0x00,
0xDF, 0xF9, 0xAE, 0x17, 0xB6, 0xD6, 0x5F, 0xFB, 0xA5, 0xFB, 0xB7, 0x96, 0x6F, 0x5D, 0xC1, 0x21,
0xE8, 0xB5, 0xFC, 0x5B, 0xBD, 0x05, 0xFE, 0x96, 0xB6, 0xF4, 0x7F, 0xE1, 0x9E, 0xAB, 0xB7, 0x1E,
0xD7, 0x84, 0xC0, 0xF1, 0xE3, 0x7B, 0x22, 0xD2, 0x77, 0x22, 0xC0, 0x2B, 0xF9, 0xB7, 0xA5, 0xFF,
0xA1, 0xED, 0x49, 0x04, 0x00, 0x28, 0xA5, 0xF5, 0xE9, 0xFA, 0x96, 0xDE, 0xFF, 0xD8, 0x19, 0xD8,
0xEC, 0x20, 0x80, 0xCA, 0x36, 0x52, 0xEA, 0x7A, 0xDB, 0x48, 0x98, 0xEA, 0xDE, 0x3F, 0x6B, 0x05,
0x80, 0xC7, 0xAB, 0x0C, 0xA8, 0xBD, 0x02, 0x00, 0x28, 0xE1, 0xE1, 0xEB, 0x3F, 0x28, 0x22, 0xC7,
0xBF, 0x0B, 0x5D, 0xBE, 0xF5, 0x99, 0x1F, 0x2D, 0xB6, 0x4F, 0x58, 0x17, 0x2F, 0xE9, 0x64, 0xA7,
0xFF, 0xFB, 0xF9, 0xFF, 0xBC, 0x76, 0x6B, 0x59, 0x97, 0x96, 0xA8, 0x0C, 0x08, 0x0D, 0xCA, 0x3A,
0xF5, 0x62, 0x6F, 0x03, 0x7F, 0x5B, 0xE2, 0x6E, 0x13, 0x02, 0xBD, 0x26, 0x02, 0x6C, 0xE0, 0xEF,
0x3D, 0xAE, 0xCB, 0x1D, 0xC7, 0xFF, 0xA7, 0x02, 0x7F, 0xAF, 0xEF, 0x7F, 0x68, 0x7B, 0x91, 0xEF,
0x1E, 0xFD, 0x3B, 0x3C, 0x2D, 0x27, 0x00, 0xA0, 0x4D, 0xB1, 0x5D, 0x03, 0xF4, 0x7E, 0x2D, 0xF5,
0xAC, 0x00, 0x59, 0x2B, 0x00, 0xC6, 0x96, 0x34, 0x53, 0x01, 0x30, 0x8F, 0xED, 0x8B, 0x1F, 0x3B,
0x22, 0x65, 0xAE, 0xF5, 0xAD, 0xB3, 0x81, 0xB7, 0x5D, 0xF6, 0xB2, 0x72, 0xA9, 0x4A, 0xF9, 0xF5,
0xFD, 0xBC, 0xFD, 0x01, 0x52, 0xB1, 0x2D, 0xFF, 0x1E, 0x0D, 0xFC, 0x6F, 0x7D, 0xF2, 0xC3, 0x5B,
0xCF, 0xFF, 0xE9, 0x26, 0x50, 0x49, 0x2F, 0x14, 0xE0, 0x87, 0x66, 0x6D, 0x09, 0xF1, 0x02, 0x7F,
0xBB, 0x3C, 0x66, 0x6C, 0x00, 0x2F, 0xC3, 0xBF, 0x26, 0xDE, 0xE7, 0xA4, 0xCB, 0x3D, 0xD3, 0xBE,
0xFE, 0x2F, 0xBC, 0xEF, 0x27, 0x5B, 0xCB, 0xDA, 0x05, 0x40, 0x9C, 0xDF, 0x8B, 0x37, 0x56, 0xC0,
0x3F, 0xFC, 0x96, 0x04, 0x00, 0x80, 0xBC, 0x96, 0xEE, 0x43, 0x3F, 0x57, 0x2B, 0xFB, 0xEF, 0x0D,
0x02, 0xA8, 0xBC, 0xC4, 0x80, 0xAD, 0xAA, 0x9B, 0x5B, 0x11, 0x90, 0xE4, 0x4A, 0xAD, 0x01, 0xBF,
0x0D, 0xE0, 0xC7, 0x96, 0x30, 0xA7, 0x1A, 0xFC, 0xCF, 0x0E, 0x5A, 0xB7, 0xE6, 0x9B, 0xB0, 0x21,
0x4B, 0x8D, 0xCE, 0xBF, 0x96, 0xCF, 0xF9, 0xEB, 0x1F, 0xFE, 0x80, 0x88, 0x88, 0xBC, 0xFE, 0xAE,
0xFF, 0x22, 0x22, 0xC7, 0x81, 0xB7, 0x5D, 0x4E, 0x85, 0x31, 0x00, 0xD0, 0x0A, 0xED, 0x12, 0xF0,
0x70, 0x81, 0x16, 0xC9, 0xD8, 0x69, 0x58, 0x6D, 0x22, 0x72, 0xEE, 0xF9, 0xCF, 0x0B, 0x60, 0xA7,
0x8C, 0x01, 0x50, 0xEB, 0x0D, 0xC8, 0x18, 0x9B, 0xD2, 0x7F, 0xB9, 0x7A, 0xE7, 0xE3, 0x76, 0x79,
0xE9, 0xC1, 0x61, 0x6B, 0x74, 0x3A, 0x11, 0x30, 0xBC, 0xFE, 0xFE, 0xBB, 0x2E, 0x17, 0xDA, 0x23,
0x00, 0x40, 0x4E, 0x76, 0x16, 0x80, 0xD0, 0x58, 0x01, 0x36, 0x81, 0xA1, 0x81, 0xBF, 0x8D, 0x9D,
0xE7, 0x56, 0x04, 0x64, 0x4D, 0xD5, 0x7B, 0x81, 0x7E, 0xEE, 0x80, 0x47, 0x3F, 0x2C, 0x2F, 0x5B,
0xB2, 0x56, 0xFA, 0xF7, 0xDA, 0x1B, 0x2F, 0xFB, 0x39, 0xE4, 0x5A, 0xBF, 0xF4, 0x18, 0x00, 0x6B,
0x11, 0x6A, 0xE1, 0xA7, 0xEF, 0x3F, 0x72, 0xD3, 0x40, 0xDF, 0x76, 0x01, 0x38, 0x0E, 0x7E, 0xB7,
0x83, 0xE0, 0x35, 0x06, 0x7B, 0xDA, 0xA2, 0x0D, 0x8C, 0x65, 0x03, 0x78, 0x5D, 0xD6, 0x00, 0x7F,
0x93, 0x08, 0x30, 0xEB, 0xED, 0x76, 0xDA, 0x65, 0xE0, 0xCA, 0xB7, 0x66, 0xD9, 0x4D, 0x00, 0xD8,
0x58, 0xBA, 0x0F, 0xFD, 0x5C, 0xB5, 0xEE, 0xBF, 0xDD, 0x1F, 0x6F, 0xAC, 0x00, 0x6F, 0x16, 0x01,
0xFB, 0x3A, 0x6A, 0xEE, 0x38, 0x3B, 0x49, 0x13, 0x00, 0xB6, 0x12, 0x60, 0x6A, 0x57, 0x00, 0xCC,
0x13, 0x6A, 0x09, 0xCB, 0xBD, 0xBE, 0x17, 0xDE, 0x5C, 0xEA, 0x73, 0x5F, 0xC7, 0x93, 0x6B, 0x20,
0x10, 0xF4, 0x6B, 0xEC, 0x31, 0xA4, 0x25, 0xFF, 0xDA, 0x05, 0x20, 0x27, 0xAF, 0xB4, 0x3F, 0x76,
0x96, 0x12, 0x9B, 0x75, 0x1F, 0xCB, 0xEB, 0x02, 0x10, 0x23, 0xD5, 0xB9, 0xB1, 0x86, 0x1B, 0x18,
0x1D, 0xE5, 0xDF, 0x0E, 0xF6, 0xA7, 0xFF, 0xDA, 0xC7, 0xED, 0x2C, 0x01, 0x3D, 0xF2, 0xFA, 0xFA,
0x87, 0x5A, 0xF6, 0xED, 0x76, 0x3A, 0x08, 0xE0, 0xDE, 0x5B, 0xF7, 0x92, 0xEC, 0x17, 0x00, 0xA0,
0x2C, 0x7B, 0x1D, 0xF7, 0xEE, 0x4D, 0xBC, 0xCA, 0x00, 0xE5, 0x55, 0x02, 0x4C, 0x95, 0x64, 0x10,
0x40, 0x1B, 0xF0, 0x7B, 0x5D, 0x02, 0xBC, 0xED, 0x09, 0x64, 0xD2, 0xF0, 0x5A, 0xE1, 0x6C, 0x97,
0x88, 0x5C, 0xEB, 0xD7, 0xE2, 0xB6, 0x67, 0x9F, 0x17, 0x11, 0x91, 0xAF, 0xCB, 0xBF, 0x13, 0x91,
0xD3, 0x63, 0x00, 0xE8, 0xFA, 0xDB, 0x0A, 0x1D, 0xB7, 0x5E, 0xC5, 0x4C, 0xAE, 0x81, 0x41, 0xD0,
0x1F, 0x6F, 0xF0, 0x3F, 0xAF, 0x12, 0xA0, 0x86, 0x44, 0x80, 0x9A, 0x3B, 0x08, 0xA0, 0xCE, 0x6C,
0xF0, 0xC2, 0x97, 0xEF, 0x10, 0x91, 0xD3, 0x81, 0xFF, 0x1C, 0x73, 0x03, 0xF8, 0xB9, 0x49, 0x8C,
0x31, 0x6C, 0x05, 0x99, 0x9D, 0xFE, 0xCF, 0x26, 0x02, 0x42, 0x8F, 0xF7, 0xCC, 0x4E, 0xEB, 0x67,
0x03, 0x7B, 0x3B, 0x28, 0x20, 0xA3, 0xFD, 0x03, 0x58, 0x5A, 0x2B, 0x7D, 0xE8, 0x3D, 0xB5, 0xEF,
0xBF, 0xD7, 0xC2, 0xAF, 0xFB, 0x69, 0x1B, 0x2F, 0xF4, 0xDE, 0x26, 0x75, 0xE9, 0xBF, 0x9A, 0x94,
0x00, 0x88, 0x2D, 0xE1, 0xB7, 0xB3, 0x04, 0xD8, 0x44, 0x81, 0xF2, 0xA6, 0xA2, 0x42, 0x5A, 0xA1,
0x1B, 0xE5, 0xB9, 0xEB, 0xD7, 0x46, 0xFB, 0xFC, 0x6F, 0x5A, 0x6D, 0x8E, 0x96, 0xEF, 0x7B, 0x57,
0xDC, 0xF6, 0x76, 0xAE, 0xE7, 0xB1, 0x42, 0xBF, 0x33, 0x5D, 0xDF, 0x7A, 0x02, 0xE6, 0xBE, 0x3B,
0x7F, 0xB5, 0xB5, 0x3C, 0xF7, 0x73, 0x43, 0x3A, 0xA1, 0x44, 0x40, 0x49, 0x5E, 0xE5, 0x51, 0xAA,
0xF3, 0x91, 0x4D, 0x04, 0xA8, 0x5E, 0x47, 0xFD, 0x57, 0xDA, 0xB7, 0x5F, 0x03, 0x7D, 0x9B, 0x18,
0xD0, 0x84, 0xC9, 0x85, 0xC7, 0xAF, 0xDC, 0x7A, 0xFE, 0xFE, 0xFE, 0x7E, 0xF3, 0xE7, 0xA6, 0xB9,
0x6C, 0x22, 0x80, 0xC0, 0x1F, 0x00, 0xFA, 0x14, 0x1A, 0xF5, 0xDF, 0x8E, 0x11, 0xE0, 0x95, 0xF8,
0xA7, 0x9A, 0x62, 0x37, 0xEB, 0x20, 0x80, 0xDE, 0xF3, 0x14, 0xA3, 0xFE, 0xA7, 0xA5, 0x07, 0x85,
0xBD, 0x21, 0xB6, 0x07, 0x4B, 0xAE, 0xF5, 0x4B, 0x8F, 0x01, 0xD0, 0xDA, 0xE8, 0xF8, 0xA1, 0x04,
0x5A, 0x28, 0xF0, 0x6F, 0x9D, 0x0D, 0xFC, 0xED, 0xE3, 0x24, 0x02, 0xF2, 0x19, 0x7B, 0x0C, 0x69,
0xE0, 0x5F, 0xA2, 0xC5, 0xBF, 0x14, 0x12, 0xCF, 0xF3, 0x30, 0xDA, 0x7F, 0xBC, 0xE3, 0x69, 0xFD,
0x80, 0xF2, 0xD6, 0x32, 0x40, 0x33, 0xF2, 0xAA, 0xB5, 0x0F, 0x7D, 0xAC, 0xDA, 0xF6, 0xDF, 0x56,
0xF0, 0x79, 0x83, 0xFB, 0xD9, 0xD2, 0xFE, 0x73, 0xE7, 0xCE, 0x6D, 0xBD, 0x4E, 0xAA, 0x80, 0xDF,
0x9A, 0x75, 0x05, 0xB7, 0x2D, 0x94, 0x36, 0xC0, 0x0F, 0x6D, 0x07, 0xA4, 0x90, 0x2B, 0xF1, 0xA0,
0x01, 0xA8, 0x17, 0xA8, 0x86, 0xB6, 0xF3, 0x78, 0x81, 0xFD, 0xBD, 0x2F, 0xFD, 0x54, 0x44, 0x44,
0xEE, 0x7A, 0xEF, 0x5F, 0xEC, 0x5C, 0xFF, 0xD5, 0x17, 0x0F, 0x5B, 0x8F, 0xEE, 0x7E, 0x1F, 0x37,
0x95, 0x98, 0x27, 0x34, 0xED, 0x5F, 0x0F, 0xB8, 0x1E, 0xED, 0xA6, 0x2D, 0xFE, 0xDA, 0xB2, 0xBF,
0x71, 0x71, 0xF8, 0xF9, 0xB9, 0x6E, 0x56, 0x5A, 0xA4, 0x2D, 0xFF, 0xCA, 0x9B, 0x0E, 0x50, 0x2B,
0x01, 0x48, 0x7E, 0x02, 0xC0, 0x3A, 0xD8, 0x16, 0x7E, 0xBB, 0x7C, 0x70, 0x70, 0x20, 0x22, 0xA7,
0xBB, 0x06, 0x94, 0x92, 0x24, 0x85, 0xCF, 0x0D, 0x54, 0x1D, 0x6C, 0xB9, 0x65, 0x68, 0xD0, 0xAC,
0xDC, 0xEB, 0x4B, 0xC9, 0xDD, 0xCF, 0x47, 0x6F, 0xC6, 0x74, 0x7A, 0xC0, 0xCD, 0x18, 0x01, 0x47,
0xCB, 0x73, 0x69, 0x60, 0x6F, 0x97, 0x35, 0x11, 0x60, 0xD7, 0xAF, 0x81, 0xBD, 0xD1, 0x1D, 0x5A,
0xBE, 0xEF, 0xCE, 0x5F, 0x71, 0x23, 0x9C, 0x89, 0x26, 0xA1, 0xBE, 0x71, 0xE3, 0x73, 0x22, 0x72,
0xBA, 0x65, 0xDF, 0x4B, 0x0C, 0x94, 0xEC, 0xFB, 0x9F, 0x1B, 0xD7, 0xAD, 0x71, 0xBC, 0xD2, 0x7F,
0xB5, 0x19, 0x33, 0xE1, 0xE8, 0x3A, 0x44, 0x22, 0xE0, 0xB8, 0xE5, 0x3F, 0x34, 0xBD, 0x9F, 0x9E,
0xFF, 0xF4, 0x79, 0x2F, 0x3C, 0x7F, 0x98, 0x18, 0x78, 0xEE, 0xC5, 0x4F, 0x88, 0x08, 0xE3, 0xBC,
0x20, 0x8D, 0xDA, 0xFA, 0x44, 0xA3, 0x2E, 0xA1, 0x80, 0xB5, 0xB5, 0xE5, 0xA5, 0x8E, 0x77, 0x5B,
0x71, 0xE3, 0xED, 0xA7, 0xED, 0x0A, 0xE0, 0xCD, 0x5C, 0x97, 0xEB, 0x5E, 0x85, 0x1A, 0xBE, 0x08,
0xF6, 0xCB, 0xAC, 0xFD, 0x24, 0xBA, 0xD4, 0xE8, 0xFC, 0x6B, 0x2D, 0x33, 0xB3, 0x81, 0xBE, 0x4D,
0x04, 0x8C, 0x65, 0x7F, 0xCC, 0xDA, 0xA2, 0x1F, 0x6B, 0x8D, 0x2D, 0xFF, 0x04, 0xFA, 0xCB, 0xF8,
0xDC, 0xF7, 0xAF, 0x13, 0x11, 0x91, 0x5B, 0x8F, 0x0E, 0x49, 0x1B, 0xF8, 0x6B, 0xA0, 0x49, 0x4B,
0xE2, 0x15, 0x00, 0x00, 0x18, 0x1C, 0x49, 0x44, 0x41, 0x54, 0x4F, 0xA5, 0x00, 0x94, 0x1D, 0x24,
0x91, 0xAE, 0x00, 0xBE, 0xCD, 0xE0, 0x7F, 0x77, 0x0D, 0x3F, 0xAE, 0x2D, 0xFF, 0x3F, 0x7C, 0xE9,
0xF0, 0xF1, 0x3B, 0xEF, 0xDB, 0x7E, 0x9E, 0x5E, 0x2A, 0x7A, 0x1F, 0x4B, 0x01, 0x00, 0x6A, 0x65,
0x4B, 0xFD, 0xBD, 0x69, 0xFF, 0xEC, 0xE3, 0xBA, 0x9D, 0x96, 0xFC, 0xE7, 0x1A, 0xEC, 0xCF, 0xC3,
0x95, 0x7B, 0x07, 0x6F, 0x04, 0xE6, 0x92, 0x23, 0x33, 0x8F, 0x61, 0x47, 0x71, 0xB6, 0x8F, 0xAB,
0x5C, 0xEB, 0x97, 0x1E, 0x03, 0x20, 0x97, 0xA9, 0x81, 0x7E, 0x2C, 0x2F, 0xA0, 0xBF, 0x3B, 0xEB,
0xBB, 0xD6, 0x61, 0xA8, 0x95, 0x7F, 0x6C, 0x97, 0x0B, 0x8C, 0x17, 0xCA, 0x28, 0x7B, 0x81, 0x7F,
0xC9, 0x96, 0xFF, 0xD8, 0x84, 0x62, 0xCD, 0x89, 0xC7, 0x9A, 0xF7, 0xCD, 0xB3, 0x39, 0xDF, 0x3B,
0xB3, 0x01, 0xD8, 0xC0, 0x5F, 0x97, 0x4F, 0x0E, 0xFE, 0x87, 0x43, 0xC7, 0x83, 0xFC, 0x7D, 0xD7,
0x2C, 0x03, 0x40, 0x5D, 0x6A, 0x69, 0xB9, 0x4F, 0xB5, 0x5C, 0xAA, 0x3A, 0xD9, 0x0B, 0xF8, 0x2D,
0xFB, 0xB8, 0x0D, 0xF0, 0xBD, 0x69, 0xFF, 0x72, 0x21, 0x01, 0x30, 0x20, 0x14, 0xE0, 0xEB, 0xE3,
0xB5, 0x26, 0x02, 0xBC, 0xD1, 0xB2, 0x4B, 0xAD, 0x07, 0xC6, 0x38, 0x99, 0x04, 0x20, 0xF8, 0x5F,
0x56, 0xA8, 0xC5, 0xBF, 0x44, 0x17, 0x80, 0x35, 0x24, 0x12, 0xD7, 0xF0, 0x37, 0x84, 0xA4, 0x98,
0x26, 0xB1, 0x17, 0x04, 0xFE, 0x00, 0xB0, 0x4E, 0x36, 0xB0, 0xB7, 0xB1, 0xA1, 0xB7, 0xAC, 0xBC,
0xC1, 0x00, 0x73, 0x77, 0x53, 0x24, 0x01, 0x30, 0x83, 0x7E, 0x99, 0xB5, 0x4C, 0x8F, 0xE7, 0x95,
0x09, 0xEA, 0xE3, 0xDE, 0xFE, 0xA5, 0x5A, 0xBF, 0x16, 0xB9, 0xFF, 0x9E, 0xB5, 0x7D, 0x5E, 0x68,
0x4F, 0xE8, 0x18, 0xFC, 0xA9, 0x7C, 0x77, 0xE7, 0xFA, 0x9C, 0xC7, 0xF0, 0xD8, 0x84, 0xEA, 0x94,
0x04, 0x6C, 0xEE, 0xDF, 0x60, 0x6D, 0x49, 0xE1, 0x29, 0x6C, 0x25, 0x40, 0xF4, 0xF3, 0xC1, 0x39,
0x1E, 0x40, 0x73, 0xEC, 0x7C, 0xF4, 0xAD, 0x2F, 0x97, 0x66, 0x07, 0xF3, 0x0B, 0xF5, 0xF9, 0xD7,
0xF5, 0xA5, 0x4B, 0xFF, 0x15, 0x09, 0x80, 0x01, 0x6B, 0x1D, 0x70, 0x27, 0x94, 0xA8, 0x98, 0xBB,
0xBE, 0x75, 0xB9, 0xBF, 0xF7, 0xB5, 0x1E, 0x57, 0xBB, 0x78, 0x2D, 0xFA, 0xA1, 0x16, 0xFF, 0xD8,
0xF5, 0x18, 0xA7, 0xC7, 0x63, 0xF0, 0xA4, 0xDE, 0xFF, 0xFE, 0x21, 0xFA, 0x99, 0xCC, 0x0D, 0x5A,
0x09, 0x7A, 0x39, 0xBE, 0x00, 0xA0, 0x57, 0x5E, 0xA0, 0xEF, 0xC5, 0x4C, 0xDE, 0xF5, 0xA2, 0x54,
0x32, 0x9D, 0x04, 0xC0, 0x80, 0xB1, 0x01, 0x6E, 0x2D, 0xA5, 0xF0, 0xB6, 0xEC, 0x34, 0xD4, 0xFF,
0x34, 0xF7, 0xFA, 0xD6, 0xE4, 0x2E, 0xDB, 0x5D, 0x43, 0xAB, 0x60, 0x2A, 0x5A, 0xF6, 0x6F, 0x03,
0xF9, 0x93, 0xCB, 0x74, 0x07, 0x48, 0xAF, 0xE7, 0x63, 0xBC, 0x44, 0xE2, 0x32, 0xF7, 0x39, 0x71,
0xEE, 0xE7, 0x3B, 0xB4, 0x7F, 0x6B, 0x3B, 0x8F, 0xCF, 0x31, 0xF7, 0xF3, 0xAD, 0xE9, 0xF7, 0x15,
0x1A, 0xBC, 0x78, 0xE9, 0xF5, 0x00, 0xB0, 0x26, 0xF6, 0x1E, 0xC3, 0x1B, 0xA3, 0x40, 0xE9, 0x39,
0x72, 0xA9, 0x19, 0x89, 0x48, 0x00, 0x0C, 0x18, 0x7B, 0x43, 0xB4, 0x74, 0x02, 0xA0, 0x87, 0xFE,
0xA6, 0x2D, 0x2B, 0x3D, 0x20, 0x49, 0x0B, 0xEE, 0xBC, 0xEF, 0x2D, 0x5B, 0x01, 0xFE, 0x50, 0x8B,
0xBF, 0x7D, 0x0E, 0xEA, 0xC5, 0x31, 0x9E, 0x57, 0xEF, 0x9F, 0xAF, 0x77, 0x03, 0x95, 0xFA, 0xF5,
0x6B, 0xFD, 0x7C, 0xC7, 0xEC, 0x9F, 0x77, 0xFF, 0xB2, 0x74, 0x83, 0x40, 0xAD, 0x63, 0x26, 0x01,
0x40, 0x0E, 0xA1, 0xD2, 0x7F, 0x5B, 0x01, 0x50, 0xAA, 0xEF, 0xBF, 0x22, 0x01, 0xB0, 0x43, 0x2B,
0x17, 0xAA, 0x56, 0xF6, 0xB3, 0x15, 0xA9, 0x7F, 0x7C, 0x94, 0xC6, 0x86, 0x11, 0xE8, 0x97, 0x15,
0x7B, 0x8C, 0x3F, 0xFA, 0x99, 0x77, 0x0C, 0x3E, 0x7E, 0xCB, 0x23, 0xBF, 0xD8, 0x5A, 0xE6, 0x18,
0xDF, 0xE6, 0x7D, 0xBE, 0x3F, 0xBE, 0xF8, 0xC5, 0xC1, 0xC7, 0xDF, 0x7F, 0xCF, 0xD7, 0x76, 0xBE,
0x5E, 0xEA, 0xCF, 0x77, 0xEC, 0x39, 0x2E, 0x34, 0x33, 0x4C, 0x6E, 0xF6, 0xFD, 0x6B, 0x3F, 0x47,
0xD7, 0xB4, 0x7F, 0xB4, 0xFC, 0x03, 0x40, 0x39, 0xF6, 0x1C, 0x68, 0x97, 0x6D, 0x62, 0x40, 0xAF,
0xA7, 0xA5, 0x13, 0x02, 0x24, 0x00, 0x06, 0xC4, 0x66, 0xDB, 0x63, 0x5B, 0xFE, 0xB9, 0x20, 0xA2,
0x57, 0x5E, 0xCB, 0xFE, 0x98, 0xED, 0xA7, 0x6E, 0x8B, 0xE9, 0xBC, 0xC0, 0xDF, 0xAE, 0xB7, 0x89,
0x00, 0x0C, 0xB3, 0x81, 0xFF, 0xB5, 0x5F, 0xBA, 0x7F, 0xFB, 0x09, 0x81, 0x04, 0xC0, 0x52, 0x34,
0xF0, 0xF4, 0x02, 0xFE, 0xA5, 0x13, 0x03, 0x63, 0xF6, 0xA1, 0x86, 0x7D, 0x2D, 0x29, 0x74, 0x9F,
0xB1, 0xF4, 0x7A, 0x00, 0x58, 0x03, 0x5B, 0xDD, 0x14, 0x1A, 0x0B, 0x40, 0x2B, 0x00, 0xF4, 0xF9,
0x5E, 0xA0, 0x9F, 0x7B, 0x4C, 0x19, 0x12, 0x00, 0x03, 0xEC, 0x34, 0x77, 0x36, 0x11, 0x30, 0x36,
0xF0, 0xF7, 0x32, 0xE4, 0x5C, 0x20, 0xDB, 0xF3, 0xF9, 0xA7, 0xAE, 0xDF, 0xB9, 0xFE, 0xA1, 0x8F,
0x3E, 0x53, 0x68, 0x4F, 0xDA, 0x71, 0x72, 0x9A, 0x3F, 0x5D, 0x8E, 0xDD, 0x4E, 0xE4, 0xF4, 0xB8,
0x00, 0x24, 0x01, 0xF2, 0x09, 0x05, 0xFE, 0xEA, 0xD3, 0xDF, 0xFC, 0xB9, 0x88, 0x88, 0xDC, 0xF2,
0xC8, 0x32, 0x7D, 0xD7, 0x5A, 0x75, 0x2A, 0xF0, 0xAF, 0x94, 0x0D, 0x96, 0xBD, 0x16, 0xE8, 0x1A,
0x82, 0x68, 0xBB, 0x0F, 0x36, 0x69, 0x11, 0x4A, 0x62, 0x2C, 0xC1, 0x5E, 0x47, 0x52, 0x5E, 0x37,
0xBC, 0xFB, 0x8A, 0xD0, 0x7D, 0x4B, 0xA9, 0xF5, 0xB5, 0x76, 0xB3, 0x00, 0x80, 0x29, 0x6C, 0x80,
0x6F, 0xCF, 0x71, 0xBA, 0x1C, 0x9A, 0x25, 0xC0, 0x26, 0x02, 0x72, 0x5F, 0xB3, 0x48, 0x00, 0xEC,
0x10, 0x3B, 0xDF, 0x7D, 0x68, 0x0E, 0x48, 0xCB, 0x9B, 0x1B, 0x12, 0xF5, 0x09, 0x05, 0xFC, 0xDE,
0xF3, 0x49, 0x04, 0x1C, 0xD2, 0x20, 0x7E, 0x28, 0x98, 0x9F, 0xF2, 0x3A, 0xFA, 0x7F, 0x92, 0x00,
0xCB, 0xFA, 0xD6, 0x67, 0xDF, 0xB9, 0xF4, 0x2E, 0x34, 0xED, 0xFE, 0xBB, 0x2E, 0x2F, 0xBD, 0x0B,
0x83, 0x34, 0x58, 0xBE, 0x70, 0xF6, 0x4A, 0x11, 0x11, 0xB9, 0xE2, 0x53, 0x87, 0xB7, 0x08, 0xAF,
0x3E, 0xFE, 0xFB, 0xAD, 0xE5, 0x07, 0x2E, 0xBE, 0x1C, 0xF5, 0x3A, 0xAA, 0x86, 0xCA, 0x80, 0x1A,
0x78, 0xD7, 0x93, 0x9C, 0xD7, 0x8D, 0x5A, 0x02, 0x7F, 0x00, 0x58, 0xA3, 0xD8, 0x18, 0x30, 0x76,
0x4C, 0x80, 0x52, 0x63, 0x01, 0x90, 0x00, 0x88, 0xE0, 0x25, 0x02, 0xE6, 0x66, 0xB2, 0xF5, 0xE0,
0x20, 0x33, 0x5E, 0x9F, 0xB1, 0x81, 0x3F, 0x86, 0x0D, 0x0D, 0xE4, 0xE7, 0x3D, 0x26, 0x72, 0xBA,
0x3A, 0x60, 0x28, 0xD0, 0x27, 0xF8, 0x5F, 0x8E, 0xB6, 0xFC, 0x93, 0x00, 0x98, 0xE7, 0x8E, 0x7B,
0xCF, 0x6E, 0x2D, 0xDF, 0x79, 0xDF, 0x42, 0x3B, 0x72, 0xC4, 0x0B, 0xFC, 0x95, 0x4D, 0x04, 0xE8,
0xF3, 0xA4, 0xA2, 0xD6, 0x76, 0xFB, 0x37, 0x0C, 0x3C, 0x41, 0x44, 0x96, 0xD9, 0xB7, 0xD8, 0xEB,
0x49, 0x8A, 0x44, 0xC0, 0xD2, 0x01, 0x7D, 0xE9, 0x80, 0x7F, 0xEC, 0xFB, 0xCD, 0xDD, 0xBF, 0x39,
0xDB, 0x93, 0x0C, 0x01, 0xD6, 0xCB, 0x6B, 0xE1, 0xD7, 0x58, 0xCF, 0x56, 0x02, 0xE8, 0xF9, 0x40,
0x9F, 0x5F, 0xFA, 0xDA, 0x44, 0x02, 0x60, 0x84, 0xD8, 0x00, 0x9D, 0xB9, 0x80, 0xD7, 0xEF, 0x9A,
0x47, 0xFF, 0x71, 0xF0, 0xF1, 0x5F, 0xDF, 0xF2, 0x27, 0x22, 0x42, 0x25, 0xC0, 0x90, 0xA1, 0x29,
0xFF, 0x6C, 0x6B, 0xFE, 0xC9, 0xC7, 0x98, 0x11, 0xA0, 0x9C, 0xD8, 0xD2, 0x7F, 0x02, 0xFF, 0x79,
0x34, 0xA3, 0xAF, 0x19, 0xFE, 0xDA, 0x78, 0x2D, 0xFE, 0xDE, 0xB2, 0x5A, 0xA2, 0xB5, 0xDD, 0xBE,
0x67, 0xA8, 0x6A, 0xE1, 0xC2, 0xE3, 0xDB, 0x49, 0x0B, 0x55, 0x53, 0xD7, 0x80, 0x14, 0x62, 0x1B,
0x2C, 0x96, 0x5E, 0x5F, 0xDA, 0xDC, 0xFB, 0xB2, 0x39, 0xDB, 0x73, 0x4F, 0x08, 0xAC, 0x5F, 0xA8,
0x85, 0xDF, 0x8E, 0x11, 0xE0, 0x5D, 0x7B, 0x4A, 0x5D, 0x93, 0xBA, 0x4A, 0x00, 0xE8, 0xCD, 0x97,
0xDE, 0x38, 0xE4, 0xBA, 0x20, 0x8D, 0x7D, 0x5D, 0xB2, 0xC2, 0xF5, 0xD1, 0xC0, 0xDD, 0xEB, 0xAB,
0xF9, 0xF5, 0x0F, 0x7F, 0x60, 0x70, 0x3B, 0x4D, 0x0C, 0x68, 0x22, 0xA0, 0x77, 0x43, 0xE5, 0xFA,
0xB6, 0x5B, 0xC0, 0x50, 0x60, 0x7F, 0x72, 0x9D, 0x6E, 0x7F, 0xB2, 0x4A, 0x60, 0x4E, 0x15, 0xC0,
0xC1, 0xC1, 0xC1, 0xE0, 0xE3, 0xDE, 0x34, 0x56, 0xB6, 0x0C, 0x6B, 0x6C, 0xF0, 0x96, 0x7A, 0x7B,
0xB4, 0x47, 0x47, 0xF9, 0xD7, 0xE3, 0xDA, 0x76, 0x01, 0xB0, 0xBF, 0x01, 0xAD, 0x10, 0xC8, 0xFD,
0xDD, 0xDB, 0x20, 0x5A, 0x83, 0xE6, 0xC7, 0x2E, 0xBF, 0x22, 0x22, 0xC7, 0x41, 0x73, 0x28, 0xA8,
0xDE, 0x3C, 0x3F, 0x50, 0x19, 0xA0, 0xE6, 0xDE, 0xE0, 0xEC, 0xED, 0xED, 0x9D, 0x6A, 0xE9, 0xF7,
0xAA, 0x16, 0xEC, 0xF2, 0x26, 0x11, 0x70, 0xFC, 0x62, 0xB3, 0xF6, 0x65, 0x97, 0xA9, 0x95, 0x64,
0x29, 0x12, 0xC8, 0x76, 0xCA, 0x44, 0x5B, 0x8E, 0x1A, 0xBA, 0x4F, 0x99, 0xBB, 0x3E, 0xF4, 0xFE,
0x63, 0xAD, 0x2D, 0x51, 0x33, 0x06, 0x53, 0x3D, 0x03, 0xF5, 0xB3, 0xE7, 0x44, 0x1B, 0xF8, 0xDB,
0x7B, 0xCC, 0xA5, 0xCF, 0x69, 0x5D, 0x25, 0x00, 0x94, 0xFD, 0xD0, 0x53, 0x27, 0x02, 0x42, 0xF3,
0xE1, 0x5A, 0x24, 0x00, 0xDA, 0x73, 0xEB, 0x33, 0x3F, 0xDA, 0xB9, 0x9E, 0xA0, 0xED, 0x58, 0xCC,
0x28, 0xFE, 0x5E, 0x17, 0x80, 0xA1, 0xD7, 0x98, 0xCA, 0x06, 0xFE, 0xA1, 0xDF, 0x9D, 0xFE, 0x8E,
0x75, 0xBB, 0xB1, 0xB3, 0x79, 0xA4, 0xDE, 0x3E, 0x87, 0xD8, 0x96, 0x7F, 0x4B, 0x47, 0xFF, 0x7F,
0xF4, 0x33, 0xEF, 0x60, 0x26, 0x80, 0x08, 0xA1, 0xE3, 0x37, 0x74, 0xFC, 0xE7, 0xF6, 0x85, 0x7B,
0xAE, 0x16, 0x91, 0xE3, 0xBE, 0xFD, 0x76, 0xD9, 0x06, 0xCD, 0xB6, 0x02, 0x40, 0x79, 0xAD, 0xED,
0xA9, 0x6F, 0x74, 0xDC, 0x32, 0xFF, 0x89, 0xAF, 0xA5, 0x09, 0x8C, 0x35, 0xF0, 0xEE, 0x3F, 0x6C,
0xBF, 0xD4, 0x5C, 0x2D, 0xFC, 0xB1, 0xEF, 0x8F, 0x7A, 0x84, 0xBE, 0x1B, 0xBB, 0x9E, 0x65, 0x96,
0x59, 0x0E, 0x9F, 0xCB, 0xBC, 0x3E, 0xFE, 0xBA, 0xAC, 0xF7, 0x76, 0xE7, 0xCE, 0x9D, 0x13, 0x91,
0x72, 0x7D, 0xFF, 0xD5, 0x19, 0x11, 0xA9, 0xB3, 0x16, 0xB1, 0xA0, 0xD4, 0x15, 0x01, 0x63, 0x2F,
0x74, 0xBD, 0x8F, 0x01, 0xA0, 0x7F, 0xBF, 0xCD, 0x96, 0x95, 0xA6, 0xDF, 0xDB, 0xD0, 0x8F, 0xCF,
0xB6, 0xC8, 0xD8, 0x1F, 0xEA, 0xAE, 0x1F, 0x6E, 0xEE, 0x8A, 0x93, 0xDA, 0x9C, 0xFC, 0x1C, 0xBD,
0xE0, 0x7F, 0xCA, 0x18, 0x00, 0xF6, 0xB5, 0x62, 0x3F, 0xD7, 0xD0, 0x6C, 0x1E, 0x36, 0x38, 0xB1,
0x2D, 0xF4, 0x5E, 0xF6, 0x56, 0x1F, 0xD7, 0x93, 0x77, 0xA9, 0xED, 0xE7, 0xFC, 0x3E, 0xEC, 0x31,
0x6E, 0x13, 0x00, 0xFF, 0xF2, 0xCA, 0xC3, 0xE3, 0xFC, 0x7F, 0xBF, 0x32, 0xDC, 0xF2, 0x38, 0xB4,
0xFE, 0x96, 0x47, 0x7E, 0xD1, 0xDD, 0x31, 0xEE, 0xF1, 0xCE, 0x21, 0xB1, 0x81, 0xBD, 0x97, 0x20,
0x4B, 0xF5, 0xF9, 0xDA, 0x3E, 0x8A, 0x1A, 0xE8, 0x87, 0x6C, 0x12, 0x01, 0x91, 0x81, 0xB7, 0x1D,
0x2C, 0x30, 0x34, 0x52, 0xBF, 0xC7, 0x56, 0x0E, 0x9C, 0xBC, 0x91, 0xF2, 0x2A, 0x00, 0xBC, 0x24,
0x86, 0x2E, 0xDB, 0xE4, 0xC5, 0xC9, 0xE0, 0x3F, 0xD5, 0xE7, 0xAB, 0xDF, 0x7F, 0xA8, 0x02, 0xE0,
0xC1, 0x8F, 0x0C, 0xFF, 0x96, 0x6F, 0xFF, 0xC1, 0xE1, 0xEB, 0xE8, 0xF5, 0x66, 0xCC, 0xF7, 0x1F,
0x4A, 0x34, 0x86, 0xCE, 0x7B, 0x5E, 0x79, 0x6A, 0xEC, 0x7A, 0x6B, 0xEE, 0xF5, 0xDC, 0xDB, 0xDF,
0x9E, 0xE8, 0x67, 0x3B, 0xF5, 0xB3, 0x8C, 0x0D, 0xF0, 0x15, 0x49, 0x1A, 0x60, 0x3A, 0x2F, 0xD9,
0x6A, 0x13, 0x01, 0x7A, 0x3E, 0xD7, 0x7B, 0x40, 0x7B, 0x5D, 0xCC, 0x9D, 0x10, 0xE8, 0xB2, 0x02,
0xC0, 0x4A, 0x5D, 0x11, 0x60, 0xBF, 0x5C, 0xCF, 0xD8, 0xE9, 0x04, 0x15, 0x27, 0xE7, 0x7A, 0xD8,
0x9B, 0x9E, 0xD2, 0x19, 0xBC, 0x56, 0x0C, 0xF5, 0xFF, 0x3F, 0x29, 0x66, 0x96, 0x00, 0xC6, 0x01,
0x48, 0xEB, 0x64, 0xF0, 0x1F, 0x0A, 0xFC, 0x51, 0xC6, 0xDC, 0xD9, 0x32, 0xA6, 0x0A, 0x05, 0xCD,
0x21, 0xA7, 0xBA, 0x00, 0x1C, 0xB1, 0x5D, 0x02, 0x94, 0x17, 0xCC, 0x79, 0xFD, 0xFA, 0x87, 0x5A,
0xE8, 0xED, 0x7B, 0x6A, 0x60, 0x6F, 0x03, 0xFD, 0x98, 0xC0, 0x3F, 0x37, 0xDB, 0x35, 0x4C, 0x03,
0x7F, 0xEF, 0x3A, 0xA1, 0xD7, 0x91, 0x87, 0x64, 0xFA, 0x75, 0x24, 0x74, 0x9F, 0x10, 0xEA, 0x8A,
0x34, 0x77, 0x3D, 0xF7, 0x29, 0xED, 0xA2, 0x5A, 0x03, 0x88, 0x67, 0x7F, 0x2F, 0xDE, 0xB4, 0x80,
0xF6, 0x71, 0xDD, 0xCE, 0x8B, 0x1B, 0x72, 0x8F, 0x1D, 0x42, 0x02, 0xE0, 0x84, 0x54, 0x89, 0x00,
0x3B, 0x08, 0x4F, 0xA8, 0x74, 0xCE, 0xE3, 0x9D, 0x84, 0x39, 0x39, 0x97, 0xA3, 0x3F, 0x4C, 0x6D,
0x91, 0xD1, 0xBE, 0xFF, 0x0F, 0x5F, 0xFF, 0x41, 0x11, 0x11, 0xB9, 0xED, 0xD9, 0xE7, 0xB7, 0x9E,
0xA7, 0x3F, 0x60, 0x7D, 0x9E, 0xAE, 0xEF, 0x95, 0x6D, 0xD9, 0x1F, 0x5B, 0xEA, 0xEF, 0x6D, 0x8F,
0x34, 0xC6, 0xB6, 0xF8, 0xEB, 0xBF, 0x94, 0xFD, 0xCF, 0xE3, 0x25, 0xC0, 0x54, 0x2B, 0x89, 0x44,
0x2F, 0xF0, 0xB7, 0x63, 0x05, 0xC8, 0x45, 0xD9, 0x7E, 0x5E, 0x60, 0x8C, 0x80, 0x53, 0xB3, 0x11,
0x5C, 0xDC, 0x7E, 0xDF, 0xFD, 0xFD, 0xFD, 0xCD, 0x36, 0x5E, 0x22, 0xC0, 0xEE, 0x8B, 0xDD, 0xE7,
0xA1, 0xD7, 0x4A, 0xCD, 0x1B, 0x2C, 0xD6, 0xF2, 0xBE, 0x6F, 0xBD, 0x8E, 0xFC, 0x6C, 0xC4, 0x7B,
0x7A, 0xF7, 0x05, 0xF6, 0xBE, 0xC3, 0xDE, 0x5C, 0xDA, 0x7E, 0xE6, 0x53, 0xD7, 0x87, 0xDE, 0xBF,
0xF7, 0x2A, 0x21, 0x00, 0xEB, 0xE2, 0x05, 0xFC, 0x96, 0x7D, 0xDC, 0x9E, 0x43, 0xED, 0x75, 0x20,
0x77, 0xD5, 0x13, 0x09, 0x80, 0x01, 0xB9, 0x12, 0x01, 0xA1, 0xE7, 0xA9, 0x50, 0x80, 0x9F, 0xB2,
0x24, 0x18, 0xF3, 0x78, 0x15, 0x00, 0x9A, 0x20, 0xE8, 0x9D, 0x0D, 0x6C, 0x74, 0x00, 0xBF, 0x50,
0x22, 0xC0, 0x2B, 0xFD, 0x27, 0x11, 0x50, 0x06, 0x15, 0x01, 0x79, 0x85, 0xA6, 0xBB, 0xBC, 0xE3,
0xDE, 0xBC, 0x3D, 0xF3, 0x8E, 0xAF, 0x71, 0x7B, 0x22, 0x12, 0x31, 0x06, 0x80, 0x09, 0xF0, 0x6D,
0xD0, 0x1D, 0x4A, 0x04, 0x28, 0x6F, 0x8C, 0x00, 0xAF, 0xA5, 0xDF, 0x1B, 0x6B, 0xE0, 0xE4, 0xDF,
0xE0, 0x05, 0xEF, 0xDE, 0x3E, 0xD9, 0xED, 0x73, 0xF0, 0x06, 0x89, 0x55, 0x9A, 0x50, 0xF6, 0xAE,
0x1F, 0xBA, 0xFE, 0x9A, 0x04, 0xFB, 0xE2, 0xDD, 0x7F, 0xD8, 0xBF, 0xDF, 0x06, 0xF8, 0x73, 0xD7,
0x87, 0xDE, 0x1F, 0xF5, 0xE2, 0x9E, 0x12, 0x88, 0x17, 0x8A, 0xE1, 0xBC, 0x65, 0xFB, 0x7C, 0x4D,
0x08, 0x94, 0x6A, 0x00, 0x20, 0x01, 0xB0, 0x43, 0xEE, 0x44, 0xC0, 0xDC, 0x4C, 0xB8, 0xED, 0x5F,
0x42, 0x66, 0x3D, 0x3D, 0xBD, 0x11, 0x53, 0x3A, 0xF8, 0x9F, 0x0D, 0xF0, 0x6B, 0x9F, 0xDE, 0xAB,
0x06, 0x43, 0x41, 0xFF, 0x50, 0x59, 0xFF, 0xD0, 0x14, 0x80, 0x76, 0x3B, 0xE4, 0xA1, 0x81, 0xBF,
0x5D, 0x26, 0x11, 0x50, 0x56, 0xE9, 0x96, 0x7F, 0x1B, 0xE8, 0x87, 0xFA, 0xFC, 0x7B, 0x01, 0xBB,
0x0D, 0xBA, 0x6D, 0xC0, 0xEF, 0x55, 0x06, 0x04, 0x67, 0x15, 0xD8, 0x61, 0x73, 0x9D, 0x36, 0xC9,
0x04, 0x2F, 0x69, 0xB1, 0x44, 0x5F, 0x72, 0x6F, 0x56, 0x18, 0x7B, 0x7D, 0x51, 0xA1, 0xD9, 0x66,
0x76, 0x89, 0x6D, 0x70, 0xF0, 0x46, 0xE9, 0x9F, 0xBB, 0x9E, 0x80, 0xBF, 0x5D, 0x04, 0xFE, 0xC0,
0x74, 0x36, 0x90, 0xF7, 0xFA, 0xFC, 0x7B, 0xEB, 0x4B, 0x5F, 0x9B, 0x48, 0x00, 0x44, 0x48, 0x9D,
0x08, 0x08, 0x61, 0xCE, 0xD8, 0xFA, 0x69, 0x22, 0x40, 0x6F, 0xE0, 0x74, 0xB0, 0xA7, 0xCD, 0xE0,
0x4D, 0x9D, 0x97, 0xFE, 0xAB, 0x98, 0xE0, 0x7D, 0x4C, 0x6B, 0x7E, 0xCA, 0x96, 0x7F, 0xEF, 0x46,
0x36, 0x96, 0xED, 0xBF, 0x55, 0x7A, 0xFB, 0x1C, 0xC6, 0x0C, 0xFE, 0x87, 0xE9, 0x6A, 0xAB, 0x60,
0x89, 0xED, 0x37, 0xAF, 0xBC, 0xFE, 0xF3, 0x5E, 0x05, 0x80, 0x57, 0xA6, 0xEF, 0x25, 0x1C, 0x76,
0xF5, 0xFD, 0x0F, 0xF1, 0xB6, 0x59, 0x22, 0xF0, 0xDF, 0x74, 0x01, 0x7B, 0x76, 0xDE, 0xF6, 0x63,
0xBA, 0x28, 0xC4, 0x36, 0x38, 0x84, 0x82, 0xBD, 0xA9, 0xEB, 0x73, 0x35, 0x78, 0x20, 0x1F, 0x02,
0x7F, 0x60, 0x3E, 0x2F, 0xD0, 0xF7, 0xCE, 0x7D, 0x5E, 0xE0, 0x5F, 0xEA, 0x5A, 0x45, 0x02, 0x60,
0x84, 0xDC, 0xD3, 0x07, 0x4E, 0x7D, 0x5D, 0x32, 0xEE, 0xF9, 0xD8, 0xC1, 0x9B, 0x34, 0xC0, 0xF7,
0x46, 0x6F, 0xC6, 0x30, 0x2F, 0xE0, 0x89, 0x1D, 0x1B, 0x60, 0xD7, 0x6B, 0x60, 0x3A, 0x0D, 0xE8,
0x09, 0xF0, 0xCB, 0x88, 0x99, 0xED, 0x62, 0x09, 0xB6, 0x65, 0xDE, 0xEB, 0x37, 0x1F, 0x0A, 0xD0,
0x83, 0x83, 0x01, 0x1E, 0xB1, 0xEF, 0xE3, 0xB5, 0xD6, 0xB7, 0xCA, 0x8E, 0xFD, 0x12, 0x6A, 0xC9,
0xB7, 0x95, 0x65, 0x29, 0xC6, 0x8E, 0xF1, 0x5A, 0xE8, 0x55, 0x68, 0x70, 0xE1, 0xB9, 0xEB, 0x43,
0xEF, 0x0F, 0x00, 0x2D, 0x88, 0xED, 0x6E, 0x6D, 0xC7, 0x41, 0xD1, 0x98, 0xB1, 0xD6, 0x19, 0x4C,
0x48, 0x00, 0x4C, 0x90, 0x3B, 0x11, 0xE0, 0xCD, 0xA3, 0xEB, 0x21, 0x01, 0x90, 0x9F, 0x4D, 0x04,
0x78, 0x6C, 0xE9, 0x66, 0xEF, 0x83, 0x00, 0xD6, 0x64, 0xEE, 0xEF, 0x64, 0xEE, 0x0D, 0x6C, 0x6D,
0x37, 0xC0, 0xB7, 0x3C, 0xF2, 0x8B, 0xCD, 0x4C, 0x00, 0x36, 0x11, 0xA0, 0x48, 0x08, 0xA4, 0x51,
0x6B, 0x37, 0x16, 0x5B, 0x3E, 0x6F, 0x07, 0xDB, 0x53, 0x73, 0x03, 0xF4, 0xD0, 0x40, 0x7D, 0xB1,
0x09, 0x86, 0x5D, 0x42, 0x63, 0x02, 0x2C, 0x29, 0x74, 0x1D, 0xB8, 0x2D, 0x61, 0x97, 0x0F, 0xEF,
0xFE, 0x21, 0x74, 0x5F, 0x51, 0x6A, 0x7D, 0x6D, 0xE7, 0x41, 0xCC, 0xFF, 0x6E, 0x96, 0xDE, 0x3E,
0x37, 0xF6, 0xAF, 0x6E, 0xB9, 0xFF, 0x7E, 0x6F, 0xF4, 0x7E, 0xDB, 0xA7, 0xDF, 0x96, 0xF6, 0x1F,
0x1C, 0x1C, 0x88, 0xC8, 0xF1, 0x34, 0x7F, 0xB5, 0x0D, 0xEE, 0x4B, 0x02, 0x60, 0x86, 0xDC, 0x89,
0x80, 0x5E, 0x7F, 0xCC, 0x35, 0xD3, 0x44, 0xC0, 0xED, 0xB2, 0x7D, 0xB3, 0x33, 0xA7, 0xCF, 0x66,
0xEF, 0x4E, 0xB6, 0x7E, 0x96, 0x9C, 0xEA, 0xCF, 0x9E, 0xCC, 0x71, 0x3A, 0xE0, 0xB7, 0x95, 0x01,
0x8C, 0xFE, 0x3F, 0x4D, 0x68, 0x76, 0x8B, 0x5A, 0xD8, 0xBE, 0xFB, 0xA9, 0xA7, 0xCE, 0x0B, 0xF5,
0xED, 0xCF, 0xD1, 0xF2, 0x5F, 0x43, 0xEB, 0xCB, 0x12, 0x15, 0x00, 0xAA, 0x74, 0xCB, 0x3F, 0xD3,
0x16, 0xB7, 0x6B, 0xE9, 0x40, 0xBE, 0xF6, 0x40, 0x96, 0xFD, 0xAB, 0x5B, 0xAE, 0xBF, 0xDF, 0x1B,
0xBD, 0xDF, 0xF6, 0xE1, 0xF7, 0xFA, 0xFC, 0xDB, 0xED, 0xD4, 0xD2, 0x09, 0x01, 0x12, 0x00, 0x09,
0xA4, 0x4E, 0x04, 0x84, 0xFA, 0x8D, 0xA8, 0xB1, 0xD3, 0x09, 0xAA, 0x5E, 0x4F, 0x0E, 0x53, 0xE8,
0x8D, 0x58, 0xE8, 0x06, 0xCE, 0xAE, 0xA7, 0xE5, 0xDF, 0x17, 0x1B, 0xFC, 0xE4, 0x2E, 0x8B, 0x8E,
0xFD, 0xFD, 0xE8, 0xF3, 0x42, 0xD3, 0x5C, 0x95, 0xDA, 0x3E, 0x25, 0x1B, 0xD0, 0x6B, 0x45, 0x80,
0xA2, 0x02, 0xA0, 0x4F, 0xA1, 0x16, 0xFA, 0x50, 0xA0, 0x1E, 0x1B, 0xE8, 0x87, 0x06, 0xF9, 0x1B,
0xD3, 0xAA, 0xFF, 0xCB, 0xCB, 0x77, 0x6F, 0x2D, 0xEB, 0xB8, 0x02, 0x7B, 0x66, 0xCA, 0xC1, 0x25,
0x94, 0xAC, 0x00, 0x50, 0xB9, 0xFA, 0xF8, 0xA7, 0x5A, 0x8F, 0x7A, 0x85, 0x46, 0x2D, 0x57, 0xDE,
0x77, 0x5C, 0x7A, 0xFB, 0xDC, 0xEB, 0xD9, 0xBF, 0xBA, 0xD7, 0x5B, 0xA9, 0xFF, 0x7E, 0xCB, 0xEB,
0xE3, 0xEF, 0x55, 0x0A, 0x78, 0x81, 0xFE, 0xD2, 0xE3, 0xBD, 0x91, 0x00, 0x48, 0x28, 0xD7, 0xAC,
0x01, 0xF6, 0x75, 0xC6, 0x06, 0xFE, 0x5E, 0x86, 0x9E, 0x0B, 0x74, 0xBC, 0x53, 0x37, 0x70, 0x13,
0x07, 0x75, 0xEA, 0xDD, 0xD0, 0xE8, 0xFE, 0x96, 0x37, 0x5B, 0x00, 0xF2, 0xD1, 0xC0, 0x5F, 0x13,
0x02, 0x36, 0x11, 0x80, 0x69, 0x5A, 0x69, 0xF9, 0xF7, 0x46, 0xD2, 0x57, 0x53, 0x07, 0xE5, 0x8B,
0x4D, 0x14, 0xB8, 0xFB, 0x33, 0xC1, 0xDB, 0xCF, 0x7E, 0x55, 0x44, 0x4E, 0x27, 0x04, 0x96, 0x2C,
0x04, 0x28, 0x59, 0x01, 0xE0, 0x5D, 0xD7, 0x43, 0xF7, 0x0D, 0xA5, 0xD6, 0x33, 0x18, 0x20, 0x80,
0x35, 0xB0, 0xE7, 0xB2, 0xB1, 0x2D, 0xFE, 0x4B, 0x57, 0xA7, 0x91, 0x00, 0xC8, 0x20, 0xF7, 0xF4,
0x81, 0xDE, 0xF3, 0x54, 0x28, 0xC0, 0xF7, 0xE6, 0xA6, 0x04, 0x4A, 0xB2, 0x03, 0x00, 0xDA, 0xC7,
0x4B, 0xF1, 0x7E, 0x5F, 0x73, 0x2B, 0x70, 0x72, 0x6F, 0x9F, 0x43, 0x28, 0xF0, 0xB7, 0x89, 0x02,
0xEC, 0x76, 0xC7, 0xBD, 0x67, 0xB7, 0x96, 0xEF, 0xBC, 0x6F, 0xF7, 0xE3, 0x4B, 0x3B, 0x35, 0x26,
0xC0, 0xD8, 0xED, 0x44, 0x37, 0xDF, 0x1B, 0x5C, 0xEF, 0xB5, 0xE4, 0xA7, 0xB8, 0x11, 0xD2, 0xC0,
0xDF, 0x2E, 0xDB, 0x44, 0xC0, 0x12, 0x96, 0xA8, 0x00, 0x50, 0xB5, 0x04, 0xFE, 0x68, 0x87, 0xBD,
0x27, 0x1C, 0x7B, 0x8F, 0x58, 0x7A, 0xFB, 0xD2, 0xD5, 0x2A, 0xEC, 0x5F, 0x5D, 0xEB, 0x43, 0xCF,
0xCF, 0x5D, 0xAD, 0x14, 0xAA, 0x38, 0xB0, 0x63, 0x04, 0x2C, 0x5D, 0xFA, 0xAF, 0x48, 0x00, 0x64,
0x94, 0x3B, 0x11, 0x30, 0x37, 0x40, 0xB0, 0xF3, 0xF6, 0x92, 0x99, 0x3F, 0x54, 0xE3, 0x20, 0x52,
0xC8, 0xC7, 0xFE, 0x4E, 0xF5, 0xE4, 0x1C, 0x7B, 0x51, 0x58, 0x7A, 0xFB, 0x29, 0xEC, 0x31, 0xFE,
0x77, 0xFF, 0x34, 0x76, 0xFB, 0xBF, 0x48, 0xB7, 0x33, 0x2B, 0xA4, 0x9F, 0xAF, 0x6D, 0x01, 0x08,
0x3D, 0x5E, 0x4A, 0xEA, 0xF7, 0x0B, 0xBD, 0xDE, 0xDC, 0xF5, 0x73, 0x9E, 0x6F, 0xBB, 0x02, 0x94,
0xB0, 0xE4, 0x35, 0x64, 0xE9, 0x80, 0x9E, 0x80, 0xBF, 0x5D, 0x4B, 0x0F, 0x74, 0x5B, 0x7B, 0x63,
0x14, 0xFB, 0x57, 0xB7, 0xDC, 0x7F, 0x7F, 0x68, 0xF0, 0x3F, 0xDB, 0x15, 0xC0, 0x8E, 0x09, 0xB0,
0x74, 0x8B, 0xBF, 0x45, 0x02, 0xA0, 0x80, 0xD4, 0x89, 0x80, 0x90, 0xA5, 0xFB, 0x95, 0xB4, 0x8A,
0xCF, 0x2D, 0x3F, 0x3B, 0xC8, 0xDF, 0x50, 0x69, 0x7F, 0xCC, 0x73, 0x30, 0x0D, 0xC7, 0x78, 0x5E,
0xB5, 0x7F, 0xBE, 0xB5, 0xEF, 0x5F, 0x48, 0xED, 0xFB, 0x5F, 0xC3, 0xFE, 0xC5, 0x36, 0x18, 0x2C,
0xBD, 0x1E, 0xF5, 0x58, 0x3A, 0x70, 0xAF, 0x3D, 0x70, 0x65, 0xFF, 0xEA, 0x56, 0xEA, 0xEF, 0x0F,
0xF5, 0xF9, 0xF7, 0x9E, 0x6F, 0x63, 0xC0, 0x5A, 0x12, 0x01, 0x24, 0x00, 0x0A, 0xCA, 0x3D, 0x6B,
0xC0, 0xD4, 0xD7, 0x25, 0x63, 0x7F, 0x28, 0xC7, 0x20, 0x6B, 0x27, 0xF5, 0x7E, 0x92, 0x16, 0x39,
0x2C, 0xEF, 0x8F, 0x09, 0xE8, 0x4F, 0x3E, 0x27, 0x76, 0x1B, 0x84, 0xD5, 0x7E, 0x8C, 0xB7, 0x3E,
0x23, 0x43, 0xEE, 0xFD, 0x9F, 0xFB, 0xF9, 0x96, 0x08, 0xC2, 0x6A, 0xFF, 0x0C, 0x72, 0xAA, 0xE9,
0xF7, 0x15, 0xFA, 0xAE, 0x97, 0x5E, 0x0F, 0x00, 0x35, 0x88, 0xED, 0x0E, 0x6D, 0x07, 0x72, 0xD6,
0x98, 0x4E, 0xFF, 0xD5, 0xEA, 0x4D, 0x9D, 0xF6, 0xAF, 0x76, 0x24, 0x00, 0x16, 0x90, 0x3B, 0x11,
0x30, 0xF6, 0x06, 0x8C, 0x04, 0x40, 0x5E, 0xB1, 0xB3, 0x3A, 0xF4, 0x22, 0xA6, 0x75, 0xBF, 0xB6,
0x41, 0xD2, 0xB0, 0x5B, 0xED, 0xC7, 0xB8, 0x2D, 0xC9, 0x6B, 0xF5, 0xF5, 0x6B, 0xFD, 0x7C, 0x4B,
0xE8, 0xF9, 0x33, 0xE8, 0xF9, 0x6F, 0x47, 0x7E, 0x8C, 0x07, 0x85, 0x9E, 0x79, 0xA3, 0xF7, 0x7B,
0xA5, 0xFE, 0x7A, 0x9D, 0x3F, 0x38, 0x38, 0x18, 0x5C, 0xDF, 0x0A, 0x12, 0x00, 0x0B, 0xCA, 0x9D,
0x08, 0xE0, 0x64, 0x3E, 0x4D, 0xEA, 0x81, 0x39, 0x18, 0x53, 0x60, 0xD8, 0xAE, 0x56, 0x7D, 0xDB,
0x0D, 0x20, 0x27, 0x7B, 0x12, 0x6F, 0x6D, 0xFB, 0x29, 0x6A, 0x3F, 0xC6, 0x73, 0xEF, 0x5F, 0xEB,
0xAF, 0xDF, 0x82, 0x9E, 0x3F, 0x03, 0xEF, 0x6F, 0x3F, 0x7F, 0xFE, 0xFC, 0xE6, 0xFF, 0x97, 0x2E,
0x5D, 0x72, 0xB7, 0xD7, 0xE7, 0xE9, 0x73, 0x5A, 0xFA, 0xDB, 0xD1, 0x2E, 0x12, 0x01, 0xE8, 0x91,
0x37, 0x7A, 0xBF, 0xED, 0xC3, 0x6F, 0x97, 0x6D, 0x57, 0x00, 0x6F, 0x3C, 0xA7, 0xA5, 0x07, 0xFB,
0xF3, 0x90, 0x00, 0xA8, 0x40, 0xEA, 0x44, 0x40, 0x6C, 0x6B, 0xC1, 0xD8, 0xE9, 0x04, 0x15, 0x17,
0x07, 0x4C, 0x35, 0xA6, 0x94, 0xBF, 0x54, 0xD9, 0xBF, 0xFD, 0x1D, 0xCC, 0xAD, 0xA0, 0x29, 0xBD,
0xFD, 0x14, 0xA9, 0xE6, 0x46, 0xD7, 0x0B, 0x5C, 0x6B, 0x99, 0x6F, 0xA0, 0xB4, 0xF3, 0xE7, 0xCF,
0xCB, 0x13, 0x4F, 0x3C, 0xB1, 0xF5, 0x98, 0x97, 0x04, 0xB8, 0x74, 0xE9, 0x92, 0x9C, 0x3F, 0x7F,
0x5E, 0xCE, 0x9F, 0x3F, 0xBF, 0x33, 0x51, 0x00, 0xE4, 0x40, 0x22, 0x00, 0x3D, 0xF3, 0xFA, 0xF8,
0x7B, 0x95, 0x02, 0x5A, 0xF2, 0x6F, 0x03, 0xFE, 0xDA, 0xEF, 0x8B, 0x48, 0x00, 0x54, 0x24, 0xD7,
0xAC, 0x01, 0xA1, 0xC1, 0x79, 0x3C, 0xDE, 0x45, 0x80, 0x8B, 0x03, 0xA6, 0x1A, 0x33, 0xB8, 0x1F,
0x03, 0x01, 0xE6, 0xA3, 0xF3, 0xBA, 0xEF, 0xCF, 0x7C, 0x9D, 0x17, 0xBE, 0x7C, 0xC7, 0xEC, 0x7D,
0xA9, 0x89, 0x5E, 0xC0, 0xAD, 0x6F, 0x7D, 0xF6, 0x9D, 0x22, 0x32, 0x7D, 0xFA, 0x43, 0x3B, 0x0F,
0xFC, 0x9C, 0x79, 0xDE, 0xD1, 0x9E, 0xA1, 0xE0, 0x5F, 0x97, 0x09, 0xF0, 0x01, 0xA0, 0x3E, 0x36,
0x76, 0xF2, 0x2A, 0x05, 0xBC, 0xE5, 0x5A, 0x06, 0xFB, 0xF3, 0x90, 0x00, 0xA8, 0x50, 0xEE, 0xE9,
0x03, 0xBD, 0xE7, 0xA9, 0x50, 0x80, 0x6F, 0xFB, 0xC5, 0xAC, 0x39, 0x11, 0xF0, 0xF9, 0xA7, 0xAE,
0x17, 0x11, 0x91, 0x87, 0x3E, 0xFA, 0xCC, 0xD6, 0xE3, 0x5E, 0x69, 0x8F, 0xF7, 0x7C, 0xCC, 0xEB,
0xD7, 0x5F, 0x62, 0x20, 0x40, 0xFD, 0x9D, 0x4C, 0x6D, 0x81, 0x5F, 0x7A, 0xFB, 0x29, 0x7E, 0x7C,
0xF1, 0x8B, 0x22, 0x22, 0xF2, 0xFE, 0x7B, 0xBE, 0xB6, 0xB5, 0xEC, 0x89, 0x7D, 0x5E, 0x09, 0x7A,
0x3C, 0xA5, 0x38, 0x2E, 0xF4, 0xF7, 0xAC, 0x81, 0xBE, 0xF5, 0xE9, 0x6F, 0xFE, 0x5C, 0x44, 0x44,
0x6E, 0x79, 0x64, 0x5C, 0x29, 0x9F, 0x06, 0xFE, 0x1A, 0xF0, 0xEB, 0xB2, 0x7D, 0xBC, 0x37, 0xA9,
0x2A, 0x50, 0xD6, 0xE0, 0x64, 0xB7, 0x00, 0x91, 0xE3, 0x84, 0xC0, 0xC9, 0x2A, 0x00, 0xA0, 0xA4,
0x35, 0xDF, 0xD3, 0x01, 0x63, 0x79, 0x63, 0x02, 0x28, 0x6F, 0x30, 0xC0, 0x5A, 0x4B, 0xFF, 0x15,
0x09, 0x80, 0x8A, 0xE5, 0x4E, 0x04, 0xCC, 0xED, 0x6A, 0xA0, 0x3F, 0x06, 0xAF, 0xD2, 0x60, 0x4D,
0x34, 0xB0, 0x7F, 0xF0, 0x23, 0xDB, 0x17, 0x46, 0xFD, 0xA1, 0xDF, 0xFE, 0x83, 0xB6, 0x47, 0x2F,
0xCF, 0x2D, 0x45, 0x7F, 0xFE, 0x5C, 0x49, 0x00, 0xFB, 0x3B, 0x1B, 0x3B, 0x90, 0xDB, 0xD2, 0xDB,
0x4F, 0x71, 0xED, 0x97, 0xEE, 0x17, 0x91, 0xE3, 0x8C, 0x75, 0x6C, 0x40, 0xAF, 0xCF, 0xB3, 0xDB,
0x97, 0xA4, 0xC7, 0xD2, 0x1D, 0xF7, 0x9E, 0x3D, 0x7A, 0x24, 0x5D, 0x22, 0x40, 0x03, 0xFD, 0xB9,
0x6C, 0x25, 0xC1, 0x9F, 0x1F, 0x55, 0x4A, 0xFC, 0xAF, 0xFF, 0xFE, 0xDF, 0x92, 0xBC, 0xFE, 0xDA,
0xAC, 0x39, 0x21, 0x70, 0xB2, 0xF5, 0xFF, 0xA6, 0x9B, 0x6E, 0x12, 0x91, 0xE3, 0xD6, 0xFF, 0x27,
0x9E, 0x78, 0x42, 0x6E, 0xBA, 0xE9, 0x26, 0xB9, 0xEA, 0xE6, 0xD7, 0x8F, 0x9F, 0x2F, 0xDB, 0xE3,
0x04, 0xA4, 0x4A, 0x00, 0x8C, 0x1D, 0xEC, 0x77, 0xCD, 0xD7, 0x73, 0x0C, 0xF3, 0x02, 0x7F, 0x2F,
0x00, 0x62, 0x99, 0xE5, 0x35, 0x2C, 0xAB, 0xD0, 0xE0, 0x7F, 0xB6, 0x2B, 0x80, 0xDE, 0xAB, 0xB5,
0x56, 0xFA, 0xAF, 0x48, 0x00, 0x34, 0x20, 0x75, 0x22, 0x20, 0xA4, 0x95, 0x83, 0xB7, 0x04, 0x6D,
0xC9, 0xD7, 0x04, 0x40, 0xEC, 0xF3, 0xB1, 0x5B, 0xEC, 0x54, 0x80, 0xCC, 0x06, 0x90, 0x97, 0x5E,
0xC0, 0xC6, 0x96, 0xF2, 0x97, 0x3C, 0x47, 0xD8, 0x2A, 0x85, 0xD4, 0xCF, 0x17, 0x39, 0x6E, 0xF9,
0xF7, 0x12, 0x00, 0x5E, 0x65, 0x80, 0x47, 0x6F, 0x04, 0x6C, 0x62, 0xE5, 0xCF, 0xFF, 0xD5, 0xBF,
0x16, 0x91, 0xE3, 0x44, 0xC0, 0xDA, 0x2B, 0x01, 0x6C, 0x60, 0xAF, 0xCB, 0xDA, 0x05, 0x45, 0xCC,
0xF2, 0xBE, 0xB3, 0xDD, 0x1A, 0x9C, 0x0C, 0xFE, 0x6F, 0xBA, 0xE9, 0xA6, 0x53, 0x5D, 0x02, 0x94,
0x4D, 0x06, 0x5C, 0xBA, 0x74, 0xA9, 0xE8, 0x20, 0x80, 0x76, 0xAA, 0x2B, 0xAC, 0x5F, 0xE8, 0xBB,
0xB6, 0xEB, 0x59, 0x66, 0x79, 0x4D, 0xCB, 0x2A, 0xD4, 0xE7, 0xDF, 0x7B, 0xBE, 0xD5, 0xCA, 0x75,
0x8B, 0x04, 0x40, 0x43, 0x72, 0xCF, 0x1A, 0x30, 0xF5, 0x75, 0xD7, 0x38, 0x8D, 0xA0, 0xCD, 0xE8,
0xD9, 0x96, 0x7F, 0x4B, 0xD7, 0x3F, 0x24, 0x67, 0x06, 0xB7, 0xEF, 0xDD, 0xD4, 0xD6, 0x59, 0xFA,
0xFE, 0xE7, 0xE1, 0x05, 0xA8, 0xB1, 0x34, 0x60, 0xD3, 0x20, 0x3B, 0x47, 0x80, 0x62, 0xF7, 0x4D,
0x97, 0x5F, 0xFC, 0xDD, 0xE1, 0xF2, 0xFD, 0x77, 0x5D, 0x0E, 0x3E, 0x3F, 0x36, 0x09, 0xA0, 0x81,
0x7F, 0x28, 0xD0, 0x7F, 0xF4, 0x33, 0xEF, 0x10, 0x91, 0xF8, 0xB1, 0x00, 0x6A, 0xEA, 0x32, 0xB1,
0x24, 0xFD, 0xFB, 0x1F, 0xBB, 0xFC, 0xCA, 0xD6, 0xBF, 0x7A, 0x1C, 0xE9, 0xB2, 0x97, 0x20, 0x68,
0xE5, 0x86, 0x2A, 0x86, 0x26, 0x02, 0x62, 0x5C, 0x75, 0xF3, 0xEB, 0x5B, 0x15, 0x01, 0x29, 0x7C,
0xE1, 0x9E, 0xAB, 0x93, 0xBE, 0x1E, 0x00, 0xB4, 0xC8, 0xB6, 0xF8, 0xDB, 0xE4, 0xA7, 0x2D, 0xED,
0x5F, 0x1B, 0x12, 0x00, 0x0D, 0xCA, 0x9D, 0x08, 0x98, 0x3B, 0x8A, 0x39, 0x80, 0xB6, 0x68, 0x49,
0x7F, 0xA8, 0x12, 0xC0, 0x06, 0x6E, 0x39, 0x69, 0xF0, 0x6C, 0xCB, 0xE9, 0x5F, 0x3C, 0x0A, 0xFC,
0xB5, 0x0B, 0x80, 0x4D, 0x04, 0x8C, 0x69, 0xF9, 0xB7, 0xAF, 0x9D, 0xAA, 0x02, 0x60, 0xAC, 0xB5,
0x56, 0x02, 0x6C, 0xAE, 0x55, 0x26, 0xB0, 0xBF, 0xE2, 0x53, 0x87, 0xB7, 0x1E, 0xAF, 0x3E, 0xFE,
0x7B, 0x11, 0x39, 0x0E, 0x4A, 0x75, 0x59, 0x8F, 0xB3, 0x56, 0x6F, 0xBC, 0xEC, 0x34, 0x7E, 0x1E,
0x5B, 0xFE, 0x3F, 0xE4, 0xAA, 0x9B, 0x5F, 0x97, 0xD7, 0x9E, 0xFC, 0xE3, 0x64, 0xFB, 0x06, 0x00,
0x08, 0x4F, 0xF7, 0xA7, 0x53, 0x34, 0x7B, 0xA3, 0xFC, 0xB7, 0x8E, 0x04, 0x40, 0xC3, 0x72, 0x27,
0x02, 0x28, 0x01, 0xF4, 0x47, 0x05, 0x4F, 0xF5, 0x7C, 0x2C, 0x47, 0x7F, 0x2F, 0x53, 0xFB, 0xDC,
0xEB, 0x76, 0x4B, 0x6D, 0x9F, 0x92, 0x5E, 0xD0, 0xF4, 0xF8, 0xB5, 0x89, 0x00, 0x1B, 0x90, 0x5D,
0x28, 0xD8, 0xA2, 0x6D, 0x03, 0x7C, 0xEF, 0x71, 0x4D, 0x62, 0xC8, 0x88, 0x04, 0x40, 0xAC, 0xD8,
0x0A, 0x01, 0x1C, 0xDB, 0xDB, 0xDB, 0x3B, 0x75, 0x8D, 0xD2, 0xC0, 0xFF, 0x81, 0x8B, 0x2F, 0x8B,
0xC8, 0x71, 0x42, 0x40, 0x97, 0x37, 0xAD, 0xD3, 0x17, 0x4F, 0xBF, 0x56, 0x4B, 0x34, 0xA8, 0x3F,
0x2F, 0xE7, 0xE5, 0xAA, 0x9B, 0x5F, 0xDF, 0x94, 0xFC, 0xDB, 0x31, 0x00, 0x00, 0x00, 0x65, 0xD9,
0x46, 0x4E, 0x2F, 0x11, 0x10, 0x1A, 0xF5, 0xBF, 0xF5, 0x84, 0x00, 0x09, 0x80, 0x15, 0x48, 0x9D,
0x08, 0x08, 0xF5, 0x7B, 0x51, 0x3D, 0xB6, 0xFC, 0xDB, 0x40, 0xC9, 0x2E, 0xA3, 0x1D, 0x73, 0x8F,
0xDF, 0xB9, 0x09, 0xB2, 0x9A, 0x12, 0x6C, 0x1A, 0x60, 0x79, 0x5D, 0x03, 0xEC, 0xE8, 0xB6, 0x25,
0xA7, 0xFF, 0xB3, 0xDD, 0x40, 0xEC, 0x20, 0x80, 0x9A, 0x08, 0xD0, 0x7F, 0x73, 0x5C, 0x94, 0x4B,
0x05, 0xFE, 0x6B, 0xAB, 0x04, 0xF0, 0xBA, 0x3E, 0xD8, 0x16, 0x7F, 0x5B, 0x96, 0x3E, 0xD4, 0x05,
0xA0, 0xB5, 0x24, 0x80, 0xC8, 0x71, 0x22, 0xE0, 0x64, 0x12, 0x40, 0xD9, 0xD6, 0xFF, 0xDF, 0x7C,
0xE7, 0x4D, 0xC1, 0x6A, 0x00, 0x00, 0xC0, 0x38, 0xB6, 0xD4, 0xDF, 0xC6, 0x38, 0x5E, 0x9F, 0x7F,
0xDD, 0xCE, 0xBB, 0xA7, 0x68, 0x7D, 0xBC, 0x34, 0x12, 0x00, 0x2B, 0x92, 0x6B, 0xD6, 0x00, 0xFB,
0x3A, 0x3D, 0x06, 0xFE, 0xC0, 0x1A, 0x6D, 0x5A, 0xCD, 0x8F, 0x02, 0xFA, 0x7D, 0xF3, 0xB8, 0xBD,
0xC0, 0x69, 0xE0, 0xAF, 0x15, 0x01, 0x35, 0x39, 0x4E, 0x14, 0xA4, 0x1B, 0x38, 0x32, 0x55, 0xE0,
0x6F, 0xC7, 0x02, 0xB0, 0x83, 0x01, 0xAE, 0xD1, 0xFE, 0xFE, 0xFE, 0x26, 0x80, 0x57, 0x17, 0x1E,
0x1F, 0xEE, 0x3A, 0xA2, 0x89, 0x00, 0x55, 0xE3, 0xF1, 0x35, 0xC6, 0x50, 0x30, 0xAF, 0x49, 0x80,
0x93, 0xCB, 0xBB, 0x9E, 0x0F, 0x00, 0x98, 0xCF, 0x0B, 0xF8, 0xAD, 0xB1, 0x2D, 0xFE, 0xAD, 0x76,
0x51, 0x53, 0x24, 0x00, 0x56, 0x28, 0xF7, 0xF4, 0x81, 0xDE, 0xF3, 0x80, 0x16, 0xC5, 0x26, 0xB4,
0xE6, 0x66, 0x7B, 0x5B, 0xC8, 0x16, 0x6B, 0x80, 0x7A, 0xAA, 0x24, 0xAE, 0x40, 0x9F, 0xFF, 0x54,
0x52, 0x0E, 0x1C, 0x69, 0xC7, 0x04, 0xA0, 0x0B, 0xC0, 0x34, 0x76, 0xB0, 0x3F, 0xEF, 0x78, 0x0A,
0x0D, 0x0E, 0xD8, 0x12, 0x2F, 0x09, 0x10, 0xF3, 0x3C, 0xBB, 0xFE, 0x8F, 0x19, 0x02, 0x00, 0x00,
0x26, 0xB1, 0x31, 0x4A, 0xEC, 0x34, 0x81, 0xF6, 0xF9, 0x7A, 0x5F, 0xD4, 0x7A, 0xE9, 0xBF, 0x22,
0x01, 0xB0, 0x62, 0xB9, 0x13, 0x01, 0x6B, 0x0E, 0xFC, 0xF5, 0x87, 0xED, 0x4D, 0xFF, 0x77, 0xFB,
0x0F, 0x6E, 0xD8, 0xB9, 0xCC, 0x74, 0x80, 0xED, 0x58, 0xF3, 0x71, 0x3C, 0x95, 0x1D, 0xFD, 0xB6,
0x86, 0x51, 0xEC, 0xBD, 0xD2, 0xFF, 0x12, 0x4A, 0x07, 0xFE, 0x6B, 0xE8, 0x0A, 0xB0, 0xB7, 0xB7,
0x77, 0x2A, 0x90, 0x57, 0xA1, 0x16, 0x7E, 0xBB, 0xBE, 0xB5, 0x2E, 0x00, 0x5E, 0x40, 0x1F, 0xD3,
0xD2, 0x4F, 0x35, 0x00, 0x00, 0xA4, 0x67, 0x03, 0x79, 0xAF, 0xCF, 0xBF, 0xB7, 0xBE, 0xF5, 0x16,
0x7F, 0x8B, 0x04, 0x40, 0x07, 0x52, 0x27, 0x02, 0x7A, 0x66, 0x13, 0x02, 0x5E, 0x82, 0x00, 0xED,
0x88, 0xAD, 0x00, 0x88, 0xAD, 0x88, 0xC9, 0xB5, 0xFD, 0xD8, 0xD9, 0x39, 0x62, 0x78, 0x7D, 0xFE,
0x43, 0x01, 0xFF, 0x12, 0x17, 0x42, 0x1B, 0xF8, 0x6B, 0x4B, 0xBF, 0x66, 0xE3, 0xEF, 0xBC, 0x6F,
0xFC, 0x6B, 0x86, 0xC6, 0xF0, 0x48, 0x1D, 0xF8, 0xF7, 0xD4, 0x15, 0xE0, 0x64, 0x17, 0x00, 0x2F,
0x11, 0xE0, 0x19, 0xFB, 0xFC, 0xDA, 0xFC, 0xE6, 0x3B, 0x6F, 0x12, 0x91, 0xF9, 0x65, 0xFE, 0xBF,
0xF9, 0xCE, 0x9B, 0xE4, 0xD2, 0xA5, 0x4B, 0x4D, 0x25, 0x3F, 0x00, 0xA0, 0x46, 0x5E, 0xA0, 0xEF,
0xC5, 0x36, 0x5E, 0xE0, 0xBF, 0x96, 0x44, 0x00, 0x09, 0x80, 0x8E, 0xE4, 0x9E, 0x35, 0x60, 0x2E,
0xBD, 0x19, 0xD7, 0x9B, 0x9D, 0x1A, 0xF6, 0x4F, 0x5B, 0xF2, 0x6D, 0xA0, 0x1F, 0x7A, 0x1C, 0x68,
0xC9, 0xD8, 0x69, 0x00, 0x97, 0x60, 0x03, 0xFF, 0x12, 0x6C, 0x17, 0x80, 0x54, 0x25, 0x7F, 0xF6,
0xF3, 0x5E, 0x6B, 0x22, 0x20, 0xB6, 0xF4, 0xDF, 0x3A, 0xF5, 0xFC, 0xC6, 0x02, 0x60, 0x9D, 0xFE,
0xEF, 0xBC, 0x9C, 0xDF, 0x7A, 0xEC, 0xE4, 0xF2, 0x55, 0x37, 0xBF, 0xBE, 0x49, 0x14, 0x9C, 0x4C,
0x0E, 0x9C, 0x7C, 0x3C, 0x34, 0x8D, 0xE0, 0x14, 0x6F, 0xBB, 0xE6, 0x2B, 0xC9, 0x5F, 0x13, 0x00,
0x6A, 0xE3, 0xB5, 0xF8, 0x6B, 0xA9, 0xBF, 0x2E, 0xB7, 0x5E, 0xCA, 0x3F, 0x15, 0x09, 0x80, 0x0E,
0xD5, 0x9E, 0x08, 0xA8, 0x69, 0xFF, 0x6C, 0xCB, 0x60, 0x68, 0x16, 0x80, 0x07, 0xDF, 0xD8, 0x7E,
0x1E, 0xEA, 0x67, 0x8F, 0x37, 0x9D, 0xFB, 0x35, 0xB6, 0xA5, 0x7E, 0xE9, 0xED, 0x53, 0xB0, 0x17,
0x48, 0x7B, 0xFC, 0x6A, 0x52, 0x4E, 0xD7, 0x2F, 0x91, 0x01, 0xF7, 0x02, 0xFF, 0x14, 0x5D, 0x01,
0x4A, 0x57, 0x02, 0xA8, 0xF7, 0x3B, 0x53, 0x16, 0x6A, 0x17, 0x00, 0xFD, 0xF7, 0x67, 0x59, 0xDE,
0x3D, 0xAF, 0xBD, 0xBD, 0xBD, 0xD1, 0xA5, 0xFF, 0xD6, 0xC9, 0xE9, 0x27, 0x5B, 0x6C, 0x05, 0xB7,
0x01, 0xFC, 0xC9, 0x65, 0x9D, 0x22, 0x50, 0xE4, 0xB8, 0x62, 0x60, 0xD7, 0xB6, 0x73, 0xD5, 0x30,
0xDD, 0x28, 0x00, 0x94, 0x12, 0x5B, 0xDA, 0xAF, 0xD6, 0xD2, 0xB7, 0x3F, 0x16, 0x09, 0x80, 0x8E,
0xD5, 0x14, 0x68, 0x0F, 0x59, 0x72, 0xFF, 0xBC, 0xC0, 0x3F, 0xB4, 0xAC, 0xDB, 0xF5, 0x76, 0x22,
0x41, 0xDB, 0xB4, 0xA5, 0xD5, 0xAB, 0x00, 0x08, 0xAD, 0x5F, 0x52, 0xCA, 0x8A, 0x80, 0xDC, 0xBF,
0x57, 0x7B, 0x5E, 0xF1, 0xCE, 0x13, 0xDA, 0xF7, 0x5F, 0x13, 0x00, 0x2D, 0x5A, 0x4B, 0x99, 0x64,
0x09, 0xB6, 0xAB, 0x40, 0x8E, 0x96, 0xFF, 0x9A, 0xA6, 0x1D, 0x05, 0x80, 0x5C, 0xBC, 0x41, 0xFC,
0x94, 0x4D, 0x0C, 0xE8, 0xB5, 0xAA, 0xB7, 0x84, 0x00, 0x09, 0x00, 0x90, 0x08, 0x18, 0xF0, 0xF0,
0xF5, 0x1F, 0xDC, 0x5A, 0x0E, 0x0D, 0xCA, 0xA5, 0xEB, 0xED, 0x76, 0x40, 0x0B, 0x6C, 0x49, 0xBA,
0x37, 0x06, 0x40, 0xAB, 0x7D, 0xB2, 0x6B, 0x31, 0xF6, 0x46, 0x42, 0xCF, 0x37, 0x2D, 0xB6, 0x7E,
0x23, 0x6C, 0xA8, 0xE5, 0xFF, 0xAA, 0x9B, 0x5F, 0x97, 0xF3, 0x72, 0x3E, 0x4B, 0x12, 0x00, 0x00,
0xD6, 0xC6, 0x8E, 0xE2, 0x1F, 0xEA, 0xEB, 0x6F, 0xBB, 0x02, 0x78, 0x81, 0x7E, 0x0B, 0x33, 0x37,
0xCD, 0x41, 0x02, 0x00, 0x1B, 0x24, 0x02, 0x8E, 0x8D, 0x1D, 0x7D, 0xBB, 0xE5, 0xD1, 0xBA, 0x01,
0x5B, 0xC1, 0x62, 0x5B, 0xFA, 0x4F, 0x96, 0x62, 0x03, 0x48, 0x83, 0xD1, 0xFE, 0x01, 0x60, 0x1E,
0x1B, 0xE0, 0xDB, 0xD8, 0x40, 0x97, 0x43, 0xB3, 0x00, 0xD8, 0x44, 0xC0, 0xDA, 0xEF, 0x77, 0x48,
0x00, 0xE0, 0x14, 0x12, 0x01, 0x40, 0x9F, 0xD6, 0x5A, 0xEA, 0x06, 0xD4, 0xE6, 0xD2, 0xA5, 0x4B,
0x22, 0x34, 0xF2, 0xBB, 0x74, 0x0C, 0x94, 0xB5, 0xDF, 0x84, 0x03, 0x98, 0xC7, 0xC6, 0x00, 0xB6,
0x22, 0x40, 0xC5, 0x8E, 0x09, 0xB0, 0xF6, 0xD2, 0x7F, 0x45, 0x02, 0x00, 0xAE, 0xDA, 0x03, 0xED,
0x5C, 0xFB, 0x47, 0xB9, 0x6D, 0x1A, 0xAD, 0x7C, 0x8E, 0x53, 0x67, 0x9D, 0xF0, 0xB2, 0xCD, 0xA5,
0xB6, 0x9F, 0xA3, 0xF6, 0xEF, 0x26, 0xF7, 0xFE, 0xB5, 0xFE, 0xFA, 0x2D, 0xE8, 0xF9, 0x33, 0xE8,
0xF9, 0x6F, 0x9F, 0xAB, 0xE4, 0xE0, 0xA7, 0xBD, 0xE1, 0xB3, 0xC5, 0x1A, 0x85, 0x5A, 0xF8, 0x35,
0x11, 0x60, 0x9F, 0xA7, 0xBF, 0x07, 0x6F, 0xBA, 0xBF, 0xB5, 0x23, 0x01, 0x80, 0xA0, 0x5E, 0x12,
0x01, 0x6B, 0xEF, 0xEF, 0x53, 0x4A, 0xAD, 0x9F, 0xA3, 0x1E, 0x27, 0xDE, 0xCD, 0xB9, 0x77, 0x73,
0xA4, 0xDB, 0xD9, 0xED, 0xC7, 0x8E, 0xF2, 0x3F, 0x77, 0xFB, 0x14, 0xA3, 0x78, 0xD7, 0xFA, 0xDD,
0xA8, 0xDC, 0xFB, 0xD7, 0xFA, 0xEB, 0xB7, 0xA0, 0xE7, 0xCF, 0xA0, 0xE7, 0xBF, 0x7D, 0x2E, 0x7B,
0x3E, 0xEC, 0xED, 0x66, 0x3C, 0x27, 0x8E, 0x4B, 0xF4, 0x20, 0xD4, 0xC2, 0x6F, 0xC7, 0x08, 0xF0,
0xCE, 0x31, 0xBD, 0x9C, 0x7B, 0xCE, 0x88, 0xC8, 0xF0, 0xBC, 0x47, 0x80, 0x63, 0x6A, 0x8B, 0xA9,
0x27, 0x75, 0xA9, 0x5F, 0xEA, 0xFD, 0x43, 0xDB, 0xD6, 0x52, 0x4A, 0xAA, 0x65, 0x69, 0x8C, 0xE6,
0x0D, 0x60, 0x2D, 0x08, 0xFC, 0xC3, 0x38, 0xF7, 0x03, 0xA7, 0xD9, 0x52, 0x7F, 0xBB, 0x7C, 0xEE,
0xDC, 0xB9, 0xAD, 0xE7, 0xF7, 0x52, 0xDA, 0x1F, 0x8B, 0x0A, 0x00, 0x8C, 0xD6, 0x4B, 0x45, 0x00,
0x00, 0x00, 0x48, 0x8F, 0xC0, 0x1F, 0xC0, 0x1C, 0x5E, 0x1F, 0x7E, 0x5D, 0x3E, 0x38, 0x38, 0x10,
0x91, 0xD3, 0x5D, 0x03, 0x70, 0x88, 0x04, 0x00, 0x26, 0xAB, 0x3D, 0xD0, 0xAE, 0x7D, 0xFF, 0x00,
0x00, 0xE8, 0x09, 0x81, 0x3F, 0x80, 0x39, 0x34, 0xA0, 0x57, 0x5E, 0x22, 0xC0, 0x76, 0x05, 0xB0,
0xE7, 0x9A, 0xDE, 0x2B, 0x02, 0x48, 0x00, 0x60, 0xB6, 0xDA, 0x03, 0xED, 0xDA, 0xF7, 0x0F, 0x00,
0x80, 0x35, 0x23, 0xF0, 0x07, 0x30, 0x85, 0x2D, 0xED, 0xF7, 0xA6, 0xFD, 0xB3, 0x8F, 0xEB, 0x76,
0xDA, 0x15, 0xC0, 0x06, 0xFC, 0xBD, 0x57, 0x04, 0x90, 0x00, 0x40, 0x32, 0xB5, 0x07, 0xDA, 0xB5,
0xEF, 0x1F, 0x00, 0x00, 0x6B, 0x42, 0xE0, 0x0F, 0x60, 0x0E, 0x2F, 0xE0, 0xB7, 0xEC, 0xE3, 0x36,
0xC0, 0xB7, 0xCB, 0xBD, 0x9F, 0x8B, 0x48, 0x00, 0x20, 0xB9, 0xDA, 0x03, 0xED, 0xDA, 0xF7, 0x0F,
0x00, 0x80, 0x96, 0x11, 0xF8, 0x03, 0x48, 0xC1, 0xDE, 0xA3, 0x87, 0x06, 0xFF, 0xB3, 0x5D, 0x04,
0xEC, 0x4C, 0x4C, 0xBD, 0x97, 0xFE, 0x2B, 0x12, 0x00, 0xC8, 0xA6, 0xF6, 0x0B, 0x7E, 0xED, 0xFB,
0x07, 0x0C, 0x61, 0x2E, 0x67, 0x00, 0xAD, 0xE0, 0x3A, 0x9B, 0x0E, 0xE7, 0x7E, 0xF4, 0xC8, 0x96,
0xF4, 0xC7, 0xF6, 0xF9, 0xD7, 0xF5, 0x94, 0xFE, 0x0F, 0x23, 0x01, 0x80, 0xEC, 0x42, 0xF3, 0xAF,
0x2F, 0xAD, 0xF6, 0xFD, 0x03, 0x44, 0xB8, 0x68, 0x01, 0x68, 0x07, 0x81, 0x7F, 0x3A, 0x9C, 0xFB,
0x01, 0x3F, 0xD0, 0xF7, 0xAA, 0x78, 0xBD, 0xDF, 0x0D, 0xE7, 0xA6, 0x43, 0x67, 0x44, 0xE4, 0x8D,
0xA5, 0x77, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xF5, 0x07, 0x4B, 0xEF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xC8, 0x8F, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x20, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x07, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0x01, 0x12,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D,
0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x07, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xD0, 0x01, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1D, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x07, 0x48, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xD0, 0x01, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x80, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x07, 0x48,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0x01, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74,
0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x40, 0x07, 0xFE, 0x70, 0xE9, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x50, 0xDE, 0x1B, 0x6F, 0xBC, 0x11,
0xF5, 0xBC, 0x33, 0x67, 0xCE, 0x64, 0xDE, 0x93, 0x79, 0x42, 0x7F, 0x47, 0xED, 0xFB, 0xDF, 0x2A,
0xFB, 0xB9, 0x7B, 0x9F, 0x73, 0xEC, 0xF3, 0x4A, 0xED, 0x4F, 0xEF, 0xA8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xA0, 0x03, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0xD5, 0x62, 0xAA, 0x2D, 0xAA,
0xFA, 0x6F, 0x6D, 0x2D, 0xAA, 0xA1, 0x16, 0xDF, 0xB1, 0xFB, 0x4F, 0x0B, 0x72, 0x59, 0xA9, 0x3F,
0xEF, 0xB9, 0xDB, 0xF7, 0xF2, 0xFD, 0x53, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x07, 0xA8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x5A, 0x6C, 0x0B, 0xBC, 0xB7, 0x3E, 0xB7, 0xB1, 0x2D, 0xFE,
0xA1, 0xF5, 0xB1, 0x7D, 0xDC, 0x63, 0xF7, 0x23, 0xD5, 0xE7, 0xD7, 0xFB, 0x18, 0x08, 0xDE, 0xDF,
0x5F, 0xFB, 0xF7, 0xBF, 0x14, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x15, 0x00,
0x00, 0x00, 0x00, 0x00, 0x26, 0xB7, 0xA4, 0x2E, 0x45, 0xF7, 0xCB, 0xF6, 0xF5, 0xF7, 0x9E, 0x17,
0x7A, 0x7C, 0x6C, 0xC5, 0x42, 0xEA, 0x31, 0x08, 0xC6, 0x5A, 0xFA, 0xFD, 0x43, 0xFB, 0x35, 0xF6,
0xFD, 0xA6, 0x1E, 0x7F, 0xF6, 0x38, 0x88, 0xD5, 0xFA, 0xF7, 0x3F, 0x15, 0x15, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x74, 0x80, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xB4, 0x60, 0x5A, 0xA1,
0xBE, 0xD9, 0xA5, 0xF6, 0x7F, 0x6C, 0x0B, 0xF4, 0xDA, 0xDF, 0x3F, 0x55, 0x8B, 0x7C, 0xED, 0xC7,
0x9F, 0x5A, 0xFA, 0xF3, 0x8F, 0x45, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0xA0, 0x02,
0x00, 0x00, 0x00, 0x00, 0xC0, 0x68, 0xB1, 0x7D, 0xF0, 0x73, 0x59, 0x7A, 0x96, 0x01, 0x6B, 0xEC,
0xFB, 0xCF, 0x1D, 0xC3, 0x60, 0xEE, 0xFB, 0xE7, 0x32, 0xB5, 0x4F, 0x7E, 0xEB, 0x6A, 0xF9, 0xFC,
0x43, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x03, 0x67, 0x44, 0xA4, 0xAF, 0xD4, 0x0C,
0x00, 0x00, 0x00, 0x80, 0xE8, 0x16, 0xDA, 0xB9, 0xA3, 0xB9, 0x2F, 0xDD, 0xF7, 0x3E, 0xF7, 0x68,
0xF4, 0xA9, 0xDE, 0x7F, 0xEA, 0xE7, 0xB6, 0xD4, 0x3C, 0xF4, 0xB1, 0xFB, 0xEB, 0x3D, 0x2F, 0x76,
0x14, 0xFD, 0xB1, 0xAF, 0x6F, 0xB5, 0xF2, 0xFD, 0x97, 0x42, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1D, 0xA0, 0x02, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x68, 0xAD, 0xB5, 0x7C, 0x02, 0xA0, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2E, 0x50, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x07,
0xFE, 0x3F, 0x46, 0xBD, 0xA4, 0x7A, 0x57, 0x3D, 0x97, 0x94, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45,
0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82
};

556
lz.c Normal file
View File

@ -0,0 +1,556 @@
/*************************************************************************
* Name: lz.c
* Author: Marcus Geelnard
* Description: LZ77 coder/decoder implementation.
* Reentrant: Yes
*
* The LZ77 compression scheme is a substitutional compression scheme
* proposed by Abraham Lempel and Jakob Ziv in 1977. It is very simple in
* its design, and uses no fancy bit level compression.
*
* This is my first attempt at an implementation of a LZ77 code/decoder.
*
* The principle of the LZ77 compression algorithm is to store repeated
* occurrences of strings as references to previous occurrences of the same
* string. The point is that the reference consumes less space than the
* string itself, provided that the string is long enough (in this
* implementation, the string has to be at least 4 bytes long, since the
* minimum coded reference is 3 bytes long). Also note that the term
* "string" refers to any kind of byte sequence (it does not have to be
* an ASCII string, for instance).
*
* The coder uses a brute force approach to finding string matches in the
* history buffer (or "sliding window", if you wish), which is very, very
* slow. I recon the complexity is somewhere between O(n^2) and O(n^3),
* depending on the input data.
*
* There is also a faster implementation that uses a large working buffer
* in which a "jump table" is stored, which is used to quickly find
* possible string matches (see the source code for LZ_CompressFast() for
* more information). The faster method is an order of magnitude faster,
* but still quite slow compared to other compression methods.
*
* The upside is that decompression is very fast, and the compression ratio
* is often very good.
*
* The reference to a string is coded as a (length,offset) pair, where the
* length indicates the length of the string, and the offset gives the
* offset from the current data position. To distinguish between string
* references and literal strings (uncompressed bytes), a string reference
* is preceded by a marker byte, which is chosen as the least common byte
* symbol in the input data stream (this marker byte is stored in the
* output stream as the first byte).
*
* Occurrences of the marker byte in the stream are encoded as the marker
* byte followed by a zero byte, which means that occurrences of the marker
* byte have to be coded with two bytes.
*
* The lengths and offsets are coded in a variable length fashion, allowing
* values of any magnitude (up to 4294967295 in this implementation).
*
* With this compression scheme, the worst case compression result is
* (257/256)*insize + 1.
*
*-------------------------------------------------------------------------
* Copyright (c) 2003-2006 Marcus Geelnard
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would
* be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
* Marcus Geelnard
* marcus.geelnard at home.se
*************************************************************************/
/*************************************************************************
* Constants used for LZ77 coding
*************************************************************************/
/* Maximum offset (can be any size < 2^31). Lower values give faster
compression, while higher values gives better compression. The default
value of 100000 is quite high. Experiment to see what works best for
you. */
#define LZ_MAX_OFFSET 100000
/*************************************************************************
* INTERNAL FUNCTIONS *
*************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <ctype.h>
#include "lz.h"
/*************************************************************************
* _LZ_StringCompare() - Return maximum length string match.
*************************************************************************/
static unsigned int _LZ_StringCompare( unsigned char * str1,
unsigned char * str2, unsigned int minlen, unsigned int maxlen )
{
unsigned int len;
for( len = minlen; (len < maxlen) && (str1[len] == str2[len]); ++ len );
return len;
}
/*************************************************************************
* _LZ_WriteVarSize() - Write unsigned integer with variable number of
* bytes depending on value.
*************************************************************************/
static int _LZ_WriteVarSize( unsigned int x, unsigned char * buf )
{
unsigned int y;
int num_bytes, i, b;
/* Determine number of bytes needed to store the number x */
y = x >> 3;
for( num_bytes = 5; num_bytes >= 2; -- num_bytes )
{
if( y & 0xfe000000 ) break;
y <<= 7;
}
/* Write all bytes, seven bits in each, with 8:th bit set for all */
/* but the last byte. */
for( i = num_bytes-1; i >= 0; -- i )
{
b = (x >> (i*7)) & 0x0000007f;
if( i > 0 )
{
b |= 0x00000080;
}
*buf ++ = (unsigned char) b;
}
/* Return number of bytes written */
return num_bytes;
}
/*************************************************************************
* _LZ_ReadVarSize() - Read unsigned integer with variable number of
* bytes depending on value.
*************************************************************************/
static int _LZ_ReadVarSize( unsigned int * x, unsigned char * buf )
{
unsigned int y, b, num_bytes;
/* Read complete value (stop when byte contains zero in 8:th bit) */
y = 0;
num_bytes = 0;
do
{
b = (unsigned int) (*buf ++);
y = (y << 7) | (b & 0x0000007f);
++ num_bytes;
}
while( b & 0x00000080 );
/* Store value in x */
*x = y;
/* Return number of bytes read */
return num_bytes;
}
/*************************************************************************
* PUBLIC FUNCTIONS *
*************************************************************************/
/*************************************************************************
* LZ_Compress() - Compress a block of data using an LZ77 coder.
* in - Input (uncompressed) buffer.
* out - Output (compressed) buffer. This buffer must be 0.4% larger
* than the input buffer, plus one byte.
* insize - Number of input bytes.
* The function returns the size of the compressed data.
*************************************************************************/
int LZ_Compress( unsigned char *in, unsigned char *out, unsigned int insize )
{
unsigned char marker, symbol;
unsigned int inpos, outpos, bytesleft, i;
unsigned int maxoffset, offset, bestoffset;
unsigned int maxlength, length, bestlength;
unsigned int histogram[ 256 ];
unsigned char *ptr1, *ptr2;
/* Do we have anything to compress? */
if( insize < 1 )
{
return 0;
}
/* Create histogram */
for( i = 0; i < 256; ++ i )
{
histogram[ i ] = 0;
}
for( i = 0; i < insize; ++ i )
{
++ histogram[ in[ i ] ];
}
/* Find the least common byte, and use it as the marker symbol */
marker = 0;
for( i = 1; i < 256; ++ i )
{
if( histogram[ i ] < histogram[ marker ] )
{
marker = (unsigned char) i;
}
}
/* Remember the marker symbol for the decoder */
out[ 0 ] = marker;
/* Start of compression */
inpos = 0;
outpos = 1;
/* Main compression loop */
bytesleft = insize;
do
{
/* Determine most distant position */
if( inpos > LZ_MAX_OFFSET ) maxoffset = LZ_MAX_OFFSET;
else maxoffset = inpos;
/* Get pointer to current position */
ptr1 = &in[ inpos ];
/* Search history window for maximum length string match */
bestlength = 3;
bestoffset = 0;
for( offset = 3; offset <= maxoffset; ++ offset )
{
/* Get pointer to candidate string */
ptr2 = &ptr1[ -(int)offset ];
/* Quickly determine if this is a candidate (for speed) */
if( (ptr1[ 0 ] == ptr2[ 0 ]) &&
(ptr1[ bestlength ] == ptr2[ bestlength ]) )
{
/* Determine maximum length for this offset */
maxlength = (bytesleft < offset ? bytesleft : offset);
/* Count maximum length match at this offset */
length = _LZ_StringCompare( ptr1, ptr2, 0, maxlength );
/* Better match than any previous match? */
if( length > bestlength )
{
bestlength = length;
bestoffset = offset;
}
}
}
/* Was there a good enough match? */
if( (bestlength >= 8) ||
((bestlength == 4) && (bestoffset <= 0x0000007f)) ||
((bestlength == 5) && (bestoffset <= 0x00003fff)) ||
((bestlength == 6) && (bestoffset <= 0x001fffff)) ||
((bestlength == 7) && (bestoffset <= 0x0fffffff)) )
{
out[ outpos ++ ] = (unsigned char) marker;
outpos += _LZ_WriteVarSize( bestlength, &out[ outpos ] );
outpos += _LZ_WriteVarSize( bestoffset, &out[ outpos ] );
inpos += bestlength;
bytesleft -= bestlength;
}
else
{
/* Output single byte (or two bytes if marker byte) */
symbol = in[ inpos ++ ];
out[ outpos ++ ] = symbol;
if( symbol == marker )
{
out[ outpos ++ ] = 0;
}
-- bytesleft;
}
}
while( bytesleft > 3 );
/* Dump remaining bytes, if any */
while( inpos < insize )
{
if( in[ inpos ] == marker )
{
out[ outpos ++ ] = marker;
out[ outpos ++ ] = 0;
}
else
{
out[ outpos ++ ] = in[ inpos ];
}
++ inpos;
}
return outpos;
}
/*************************************************************************
* LZ_CompressFast() - Compress a block of data using an LZ77 coder.
* in - Input (uncompressed) buffer.
* out - Output (compressed) buffer. This buffer must be 0.4% larger
* than the input buffer, plus one byte.
* insize - Number of input bytes.
* work - Pointer to a temporary buffer (internal working buffer), which
* must be able to hold (insize+65536) unsigned integers.
* The function returns the size of the compressed data.
*************************************************************************/
int LZ_CompressFast( unsigned char *in, unsigned char *out, unsigned int insize)
{
unsigned char marker, symbol;
unsigned int inpos, outpos, bytesleft, i, index, symbols;
unsigned int offset, bestoffset;
unsigned int maxlength, length, bestlength;
unsigned int histogram[ 256 ], *lastindex, *jumptable;
unsigned char *ptr1, *ptr2;
unsigned int *work;
/* Do we have anything to compress? */
if( insize < 1 )
{
return 0;
}
if(!(work = malloc(insize+65536 * sizeof(unsigned int))))
{
//printf("Could not allocate compression buffer\n");
//exit(0);
return 0;
}
/* Assign arrays to the working area */
lastindex = work;
jumptable = &work[ 65536 ];
/* Build a "jump table". Here is how the jump table works:
jumptable[i] points to the nearest previous occurrence of the same
symbol pair as in[i]:in[i+1], so in[i] == in[jumptable[i]] and
in[i+1] == in[jumptable[i]+1], and so on... Following the jump table
gives a dramatic boost for the string search'n'match loop compared
to doing a brute force search. The jump table is built in O(n) time,
so it is a cheap operation in terms of time, but it is expensice in
terms of memory consumption. */
for( i = 0; i < 65536; ++ i )
{
lastindex[ i ] = 0xffffffff;
}
for( i = 0; i < insize-1; ++ i )
{
symbols = (((unsigned int)in[i]) << 8) | ((unsigned int)in[i+1]);
index = lastindex[ symbols ];
lastindex[ symbols ] = i;
jumptable[ i ] = index;
}
jumptable[ insize-1 ] = 0xffffffff;
/* Create histogram */
for( i = 0; i < 256; ++ i )
{
histogram[ i ] = 0;
}
for( i = 0; i < insize; ++ i )
{
++ histogram[ in[ i ] ];
}
/* Find the least common byte, and use it as the marker symbol */
marker = 0;
for( i = 1; i < 256; ++ i )
{
if( histogram[ i ] < histogram[ marker ] )
{
marker = (unsigned char) i;
}
}
/* Remember the marker symbol for the decoder */
out[ 0 ] = marker;
/* Start of compression */
inpos = 0;
outpos = 1;
/* Main compression loop */
bytesleft = insize;
do
{
/* Get pointer to current position */
ptr1 = &in[ inpos ];
/* Search history window for maximum length string match */
bestlength = 3;
bestoffset = 0;
index = jumptable[ inpos ];
while( (index != 0xffffffff) && ((inpos - index) < LZ_MAX_OFFSET) )
{
/* Get pointer to candidate string */
ptr2 = &in[ index ];
/* Quickly determine if this is a candidate (for speed) */
if( ptr2[ bestlength ] == ptr1[ bestlength ] )
{
/* Determine maximum length for this offset */
offset = inpos - index;
maxlength = (bytesleft < offset ? bytesleft : offset);
/* Count maximum length match at this offset */
length = _LZ_StringCompare( ptr1, ptr2, 2, maxlength );
/* Better match than any previous match? */
if( length > bestlength )
{
bestlength = length;
bestoffset = offset;
}
}
/* Get next possible index from jump table */
index = jumptable[ index ];
}
/* Was there a good enough match? */
if( (bestlength >= 8) ||
((bestlength == 4) && (bestoffset <= 0x0000007f)) ||
((bestlength == 5) && (bestoffset <= 0x00003fff)) ||
((bestlength == 6) && (bestoffset <= 0x001fffff)) ||
((bestlength == 7) && (bestoffset <= 0x0fffffff)) )
{
out[ outpos ++ ] = (unsigned char) marker;
outpos += _LZ_WriteVarSize( bestlength, &out[ outpos ] );
outpos += _LZ_WriteVarSize( bestoffset, &out[ outpos ] );
inpos += bestlength;
bytesleft -= bestlength;
}
else
{
/* Output single byte (or two bytes if marker byte) */
symbol = in[ inpos ++ ];
out[ outpos ++ ] = symbol;
if( symbol == marker )
{
out[ outpos ++ ] = 0;
}
-- bytesleft;
}
}
while( bytesleft > 3 );
/* Dump remaining bytes, if any */
while( inpos < insize )
{
if( in[ inpos ] == marker )
{
out[ outpos ++ ] = marker;
out[ outpos ++ ] = 0;
}
else
{
out[ outpos ++ ] = in[ inpos ];
}
++ inpos;
}
free(work);
return outpos;
}
/*************************************************************************
* LZ_Uncompress() - Uncompress a block of data using an LZ77 decoder.
* in - Input (compressed) buffer.
* out - Output (uncompressed) buffer. This buffer must be large
* enough to hold the uncompressed data.
* insize - Number of input bytes.
*************************************************************************/
int LZ_Uncompress( unsigned char *in, unsigned char *out, unsigned int insize )
{
unsigned char marker, symbol;
unsigned int i, inpos, outpos, length, offset;
/* Do we have anything to uncompress? */
if( insize < 1 )
{
return 0;
}
/* Get marker symbol from input stream */
marker = in[ 0 ];
inpos = 1;
/* Main decompression loop */
outpos = 0;
do
{
symbol = in[ inpos ++ ];
if( symbol == marker )
{
/* We had a marker byte */
if( in[ inpos ] == 0 )
{
/* It was a single occurrence of the marker byte */
out[ outpos ++ ] = marker;
++ inpos;
}
else
{
/* Extract true length and offset */
inpos += _LZ_ReadVarSize( &length, &in[ inpos ] );
inpos += _LZ_ReadVarSize( &offset, &in[ inpos ] );
/* Copy corresponding data from history window */
for( i = 0; i < length; ++ i )
{
out[ outpos ] = out[ outpos - offset ];
++ outpos;
}
}
}
else
{
/* No marker, plain copy */
out[ outpos ++ ] = symbol;
}
}
while( inpos < insize );
return outpos;
}

53
lz.h Normal file
View File

@ -0,0 +1,53 @@
/*************************************************************************
* Name: lz.h
* Author: Marcus Geelnard
* Description: LZ77 coder/decoder interface.
* Reentrant: Yes
*-------------------------------------------------------------------------
* Copyright (c) 2003-2006 Marcus Geelnard
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would
* be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
* Marcus Geelnard
* marcus.geelnard at home.se
*************************************************************************/
#ifndef _lz_h_
#define _lz_h_
#ifdef __cplusplus
extern "C" {
#endif
/*************************************************************************
* Function prototypes
*************************************************************************/
int LZ_Compress( unsigned char *in, unsigned char *out, unsigned int insize );
int LZ_CompressFast( unsigned char *in, unsigned char *out, unsigned int insize);
int LZ_Uncompress( unsigned char *in, unsigned char *out, unsigned int insize );
#ifdef __cplusplus
}
#endif
#endif /* _lz_h_ */

376
m6502.cpp Normal file
View File

@ -0,0 +1,376 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#include "m6502.h"
M6502::OpcodeCycleFunction M6502::opcodeFunctions[256] =
{
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
&M6502::BRK,&M6502::ORA,&M6502::JAM,&M6502::SLO,&M6502::NOP,&M6502::ORA,&M6502::ASL,&M6502::SLO,&M6502::PHP,&M6502::ORA,&M6502::ASL,&M6502::ANC,&M6502::NOP,&M6502::ORA,&M6502::ASL,&M6502::SLO,// 0
&M6502::BPL,&M6502::ORA,&M6502::JAM,&M6502::SLO,&M6502::NOP,&M6502::ORA,&M6502::ASL,&M6502::SLO,&M6502::CLC,&M6502::ORA,&M6502::NOP,&M6502::SLO,&M6502::NOP,&M6502::ORA,&M6502::ASL,&M6502::SLO,// 1
&M6502::JSR,&M6502::AND,&M6502::JAM,&M6502::RLA,&M6502::BIT,&M6502::AND,&M6502::ROL,&M6502::RLA,&M6502::PLP,&M6502::AND,&M6502::ROL,&M6502::ANC,&M6502::BIT,&M6502::AND,&M6502::ROL,&M6502::RLA,// 2
&M6502::BMI,&M6502::AND,&M6502::JAM,&M6502::RLA,&M6502::NOP,&M6502::AND,&M6502::ROL,&M6502::RLA,&M6502::SEC,&M6502::AND,&M6502::NOP,&M6502::RLA,&M6502::NOP,&M6502::AND,&M6502::ROL,&M6502::RLA,// 3
&M6502::RTI,&M6502::EOR,&M6502::JAM,&M6502::SRE,&M6502::NOP,&M6502::EOR,&M6502::LSR,&M6502::SRE,&M6502::PHA,&M6502::EOR,&M6502::LSR,&M6502::ASR,&M6502::JMP,&M6502::EOR,&M6502::LSR,&M6502::SRE,// 4
&M6502::BVC,&M6502::EOR,&M6502::JAM,&M6502::SRE,&M6502::NOP,&M6502::EOR,&M6502::LSR,&M6502::SRE,&M6502::CLI,&M6502::EOR,&M6502::NOP,&M6502::SRE,&M6502::NOP,&M6502::EOR,&M6502::LSR,&M6502::SRE,// 5
&M6502::RTS,&M6502::ADC,&M6502::JAM,&M6502::RRA,&M6502::NOP,&M6502::ADC,&M6502::ROR,&M6502::RRA,&M6502::PLA,&M6502::ADC,&M6502::ROR,&M6502::ARR,&M6502::JMP,&M6502::ADC,&M6502::ROR,&M6502::RRA,// 6
&M6502::BVS,&M6502::ADC,&M6502::JAM,&M6502::RRA,&M6502::NOP,&M6502::ADC,&M6502::ROR,&M6502::RRA,&M6502::SEI,&M6502::ADC,&M6502::NOP,&M6502::RRA,&M6502::NOP,&M6502::ADC,&M6502::ROR,&M6502::RRA,// 7
&M6502::NOP,&M6502::STA,&M6502::NOP,&M6502::SAX,&M6502::STY,&M6502::STA,&M6502::STX,&M6502::SAX,&M6502::DEY,&M6502::NOP,&M6502::TXA,&M6502::XAA,&M6502::STY,&M6502::STA,&M6502::STX,&M6502::SAX,// 8
&M6502::BCC,&M6502::STA,&M6502::JAM,&M6502::SHA,&M6502::STY,&M6502::STA,&M6502::STX,&M6502::SAX,&M6502::TYA,&M6502::STA,&M6502::TXS,&M6502::SHS,&M6502::SHY,&M6502::STA,&M6502::SHX,&M6502::SHA,// 9
&M6502::LDY,&M6502::LDA,&M6502::LDX,&M6502::LAX,&M6502::LDY,&M6502::LDA,&M6502::LDX,&M6502::LAX,&M6502::TAY,&M6502::LDA,&M6502::TAX,&M6502::LXA,&M6502::LDY,&M6502::LDA,&M6502::LDX,&M6502::LAX,// A
&M6502::BCS,&M6502::LDA,&M6502::JAM,&M6502::LAX,&M6502::LDY,&M6502::LDA,&M6502::LDX,&M6502::LAX,&M6502::CLV,&M6502::LDA,&M6502::TSX,&M6502::LAS,&M6502::LDY,&M6502::LDA,&M6502::LDX,&M6502::LAX,// B
&M6502::CPY,&M6502::CMP,&M6502::NOP,&M6502::DCP,&M6502::CPY,&M6502::CMP,&M6502::DEC,&M6502::DCP,&M6502::INY,&M6502::CMP,&M6502::DEX,&M6502::SBX,&M6502::CPY,&M6502::CMP,&M6502::DEC,&M6502::DCP,// C
&M6502::BNE,&M6502::CMP,&M6502::JAM,&M6502::DCP,&M6502::NOP,&M6502::CMP,&M6502::DEC,&M6502::DCP,&M6502::CLD,&M6502::CMP,&M6502::NOP,&M6502::DCP,&M6502::NOP,&M6502::CMP,&M6502::DEC,&M6502::DCP,// D
&M6502::CPX,&M6502::SBC,&M6502::NOP,&M6502::ISB,&M6502::CPX,&M6502::SBC,&M6502::INC,&M6502::ISB,&M6502::INX,&M6502::SBC,&M6502::NOP,&M6502::SBC,&M6502::CPX,&M6502::SBC,&M6502::INC,&M6502::ISB,// E
&M6502::BEQ,&M6502::SBC,&M6502::JAM,&M6502::ISB,&M6502::NOP,&M6502::SBC,&M6502::INC,&M6502::ISB,&M6502::SED,&M6502::SBC,&M6502::NOP,&M6502::ISB,&M6502::NOP,&M6502::SBC,&M6502::INC,&M6502::ISB // F
};
M6502::AddressModeCycleFunction M6502::T1AddressModeFunctions[256] =
{
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
&M6502::brk_5_4_T1,&M6502::idx_2_4_T1,&M6502::sb_jam_T1, &M6502::idx_Undoc_T1,&M6502::zp_2_1_T1, &M6502::zp_2_1_T1, &M6502::zp_4_1_T1, &M6502::zp_4_1_T1, &M6502::ph_5_1_T1,&M6502::imm_2_1_T1, &M6502::sb_1_T1,&M6502::imm_2_1_T1, &M6502::abs_2_3_T1, &M6502::abs_2_3_T1, &M6502::abs_4_2_T1, &M6502::abs_4_2_T1, //0
&M6502::rel_5_8_T1,&M6502::idy_2_7_T1,&M6502::sb_jam_T1, &M6502::idy_Undoc_T1,&M6502::zpx_2_6_T1,&M6502::zpx_2_6_T1,&M6502::zpx_4_3_T1,&M6502::zpx_4_3_T1,&M6502::sb_1_T1, &M6502::absy_2_5_T1,&M6502::sb_1_T1,&M6502::absy_4_4_T1,&M6502::absx_2_5_T1,&M6502::absx_2_5_T1,&M6502::absx_4_4_T1,&M6502::absx_4_4_T1,//1
&M6502::jsr_5_3_T1,&M6502::idx_2_4_T1,&M6502::sb_jam_T1, &M6502::idx_Undoc_T1,&M6502::zp_2_1_T1, &M6502::zp_2_1_T1, &M6502::zp_4_1_T1, &M6502::zp_4_1_T1, &M6502::pl_5_2_T1,&M6502::imm_2_1_T1, &M6502::sb_1_T1,&M6502::imm_2_1_T1, &M6502::abs_2_3_T1, &M6502::abs_2_3_T1, &M6502::abs_4_2_T1, &M6502::abs_4_2_T1, //2
&M6502::rel_5_8_T1,&M6502::idy_2_7_T1,&M6502::sb_jam_T1, &M6502::idy_Undoc_T1,&M6502::zpx_2_6_T1,&M6502::zpx_2_6_T1,&M6502::zpx_4_3_T1,&M6502::zpx_4_3_T1,&M6502::sb_1_T1, &M6502::absy_2_5_T1,&M6502::sb_1_T1,&M6502::absy_4_4_T1,&M6502::absx_2_5_T1,&M6502::absx_2_5_T1,&M6502::absx_4_4_T1,&M6502::absx_4_4_T1,//3
&M6502::rti_5_5_T1,&M6502::idx_2_4_T1,&M6502::sb_jam_T1, &M6502::idx_Undoc_T1,&M6502::zp_2_1_T1, &M6502::zp_2_1_T1, &M6502::zp_4_1_T1, &M6502::zp_4_1_T1, &M6502::ph_5_1_T1,&M6502::imm_2_1_T1, &M6502::sb_1_T1,&M6502::imm_2_1_T1, &M6502::abs5_6_1_T1,&M6502::abs_2_3_T1, &M6502::abs_4_2_T1, &M6502::abs_4_2_T1, //4
&M6502::rel_5_8_T1,&M6502::idy_2_7_T1,&M6502::sb_jam_T1, &M6502::idy_Undoc_T1,&M6502::zpx_2_6_T1,&M6502::zpx_2_6_T1,&M6502::zpx_4_3_T1,&M6502::zpx_4_3_T1,&M6502::sb_1_T1, &M6502::absy_2_5_T1,&M6502::sb_1_T1,&M6502::absy_4_4_T1,&M6502::absx_2_5_T1,&M6502::absx_2_5_T1,&M6502::absx_4_4_T1,&M6502::absx_4_4_T1,//5
&M6502::rts_5_7_T1,&M6502::idx_2_4_T1,&M6502::sb_jam_T1, &M6502::idx_Undoc_T1,&M6502::zp_2_1_T1, &M6502::zp_2_1_T1, &M6502::zp_4_1_T1, &M6502::zp_4_1_T1, &M6502::pl_5_2_T1,&M6502::imm_2_1_T1, &M6502::sb_1_T1,&M6502::imm_2_1_T1, &M6502::abs5_6_2_T1,&M6502::abs_2_3_T1, &M6502::abs_4_2_T1, &M6502::abs_4_2_T1, //6
&M6502::rel_5_8_T1,&M6502::idy_2_7_T1,&M6502::sb_jam_T1, &M6502::idy_Undoc_T1,&M6502::zpx_2_6_T1,&M6502::zpx_2_6_T1,&M6502::zpx_4_3_T1,&M6502::zpx_4_3_T1,&M6502::sb_1_T1, &M6502::absy_2_5_T1,&M6502::sb_1_T1,&M6502::absy_4_4_T1,&M6502::absx_2_5_T1,&M6502::absx_2_5_T1,&M6502::absx_4_4_T1,&M6502::absx_4_4_T1,//7
&M6502::imm_2_1_T1,&M6502::idx_3_3_T1,&M6502::imm_2_1_T1,&M6502::idx_3_3_T1, &M6502::zp_3_1_T1, &M6502::zp_3_1_T1, &M6502::zp_2_1_T1, &M6502::zp_3_1_T1, &M6502::sb_1_T1, &M6502::imm_2_1_T1, &M6502::sb_1_T1,&M6502::imm_2_1_T1, &M6502::abs_3_2_T1, &M6502::abs_3_2_T1, &M6502::abs_3_2_T1, &M6502::abs_3_2_T1, //8
&M6502::rel_5_8_T1,&M6502::idy_3_6_T1,&M6502::sb_jam_T1, &M6502::idy_3_6_T1, &M6502::zpx_3_5_T1,&M6502::zpx_3_5_T1,&M6502::zpy_3_5_T1,&M6502::zpy_3_5_T1,&M6502::sb_1_T1, &M6502::absy_3_4_T1,&M6502::sb_1_T1,&M6502::absy_3_4_T1,&M6502::absx_3_4_T1,&M6502::absx_3_4_T1,&M6502::absy_3_4_T1,&M6502::absy_3_4_T1,//9
&M6502::imm_2_1_T1,&M6502::idx_2_4_T1,&M6502::imm_2_1_T1,&M6502::idx_2_4_T1, &M6502::zp_2_1_T1, &M6502::zp_2_1_T1, &M6502::zp_2_1_T1, &M6502::zp_2_1_T1, &M6502::sb_1_T1, &M6502::imm_2_1_T1, &M6502::sb_1_T1,&M6502::imm_2_1_T1, &M6502::abs_2_3_T1, &M6502::abs_2_3_T1, &M6502::abs_2_3_T1, &M6502::abs_2_3_T1, //A
&M6502::rel_5_8_T1,&M6502::idy_2_7_T1,&M6502::sb_jam_T1, &M6502::idy_2_7_T1, &M6502::zpx_2_6_T1,&M6502::zpx_2_6_T1,&M6502::zpy_2_6_T1,&M6502::zpy_2_6_T1,&M6502::sb_1_T1, &M6502::absy_2_5_T1,&M6502::sb_1_T1,&M6502::absy_4_4_T1,&M6502::absx_2_5_T1,&M6502::absx_2_5_T1,&M6502::absy_2_5_T1,&M6502::absy_2_5_T1,//B
&M6502::imm_2_1_T1,&M6502::idx_2_4_T1,&M6502::imm_2_1_T1,&M6502::idx_Undoc_T1,&M6502::zp_2_1_T1, &M6502::zp_2_1_T1, &M6502::zp_4_1_T1, &M6502::zp_4_1_T1, &M6502::sb_1_T1, &M6502::imm_2_1_T1, &M6502::sb_1_T1,&M6502::imm_2_1_T1, &M6502::abs_2_3_T1, &M6502::abs_2_3_T1, &M6502::abs_4_2_T1, &M6502::abs_4_2_T1, //C
&M6502::rel_5_8_T1,&M6502::idy_2_7_T1,&M6502::sb_jam_T1, &M6502::idy_Undoc_T1,&M6502::zpx_2_6_T1,&M6502::zpx_2_6_T1,&M6502::zpx_4_3_T1,&M6502::zpx_4_3_T1,&M6502::sb_1_T1, &M6502::absy_2_5_T1,&M6502::sb_1_T1,&M6502::absy_4_4_T1,&M6502::absx_2_5_T1,&M6502::absx_2_5_T1,&M6502::absx_4_4_T1,&M6502::absx_4_4_T1,//D
&M6502::imm_2_1_T1,&M6502::idx_2_4_T1,&M6502::imm_2_1_T1,&M6502::idx_Undoc_T1,&M6502::zp_2_1_T1, &M6502::zp_2_1_T1, &M6502::zp_4_1_T1, &M6502::zp_4_1_T1, &M6502::sb_1_T1, &M6502::imm_2_1_T1, &M6502::sb_1_T1,&M6502::imm_2_1_T1, &M6502::abs_2_3_T1, &M6502::abs_2_3_T1, &M6502::abs_4_2_T1, &M6502::abs_4_2_T1, //E
&M6502::rel_5_8_T1,&M6502::idy_2_7_T1,&M6502::sb_jam_T1, &M6502::idy_Undoc_T1,&M6502::zpx_2_6_T1,&M6502::zpx_2_6_T1,&M6502::zpx_4_3_T1,&M6502::zpx_4_3_T1,&M6502::sb_1_T1, &M6502::absy_2_5_T1,&M6502::sb_1_T1,&M6502::absy_4_4_T1,&M6502::absx_2_5_T1,&M6502::absx_2_5_T1,&M6502::absx_4_4_T1,&M6502::absx_4_4_T1 //F
};
void M6502::ADC(void)
{
u16 result;
result = a + value + (status & FLAG_CARRY);
EstablishZ(result);
if (status & FLAG_DECIMAL)
{
result = (a & 0xf) + (value & 0xf) + (status & FLAG_CARRY);
if (result > 0x9) result += 0x6;
if (result <= 0x0f) result = (result & 0xf) + (a & 0xf0) + (value & 0xf0);
else result = (result & 0xf) + (a & 0xf0) + (value & 0xf0) + 0x10;
EstablishV(result, value);
EstablishN(result);
if ((result & 0x1f0) > 0x90) result += 0x60;
EstablishC(result);
}
else
{
EstablishC(result);
EstablishV(result, value);
EstablishN(result);
}
a = (u8)result;
}
void M6502::ARR(void)
{
u16 result = a & value;
u16 carry = status & FLAG_CARRY;
if (status & FLAG_DECIMAL)
{
u16 resultDEC = result;
resultDEC |= carry << 8;
resultDEC >>= 1;
SetN(carry);
SetZ(resultDEC == 0);
SetV((resultDEC ^ result) & 0x40);
if (((result & 0xf) + (result & 0x1)) > 0x5) resultDEC = (resultDEC & 0xf0) | ((resultDEC + 0x6) & 0xf);
if (((result & 0xf0) + (result & 0x10)) > 0x50)
{
resultDEC = (resultDEC & 0x0f) | ((resultDEC + 0x60) & 0xf0);
SetC();
}
else ClearC();
a = (u8)resultDEC;
}
else
{
result |= carry << 8;
result >>= 1;
EstablishNZ(result);
u16 and40 = result & 0x40;
SetC(and40 != 0);
SetV(and40 ^ ((result & 0x20) << 1));
a = (u8)result;
}
}
void M6502::SBC(void)
{
u16 result = a - value - ((status & FLAG_CARRY) ? 0 : 1);
if (status & FLAG_DECIMAL)
{
u16 tmp_a;
tmp_a = (a & 0xf) - (value & 0xf) - ((status & FLAG_CARRY) ? 0 : 1);
if (tmp_a & 0x10) tmp_a = ((tmp_a - 6) & 0xf) | ((a & 0xf0) - (value & 0xf0) - 0x10);
else tmp_a = (tmp_a & 0xf) | ((a & 0xf0) - (value & 0xf0));
if (tmp_a & 0x100) tmp_a -= 0x60;
SetC(result < 0x100);
EstablishV(result, value ^ 0xff);
EstablishNZ(result);
a = (u8)tmp_a;
}
else
{
EstablishNZ(result);
SetC(result < 0x100);
EstablishV(result, value ^ 0xff);
a = (u8)result;
}
}
void M6502::absx_2_5_T3(void)
{
u16 startpage = ea & 0xFF00;
ea += x;
if (startpage != (ea & 0xFF00))
{
BUS_READ(startpage | (ea & 0xff));
addressModeCycleFn = &M6502::absx_2_5_T4;
}
else
{
value = BUS_READ(ea);
ExecuteOpcode();
}
}
void M6502::absy_2_5_T3(void)
{
u16 startpage = ea & 0xFF00;
ea += y;
if (startpage != (ea & 0xFF00))
{
BUS_READ(startpage | (ea & 0xff));
addressModeCycleFn = &M6502::absy_2_5_T4;
}
else
{
value = BUS_READ(ea);
ExecuteOpcode();
}
}
void M6502::idy_2_7_T4(void)
{
u16 startpage = ea & 0xFF00;
ea += y;
if (startpage != (ea & 0xFF00))
{
BUS_READ(startpage | (ea & 0xff));
addressModeCycleFn = &M6502::idy_2_7_T5;
}
else
{
value = BUS_READ(ea);
ExecuteOpcode();
}
}
void M6502::rel_5_8_T2(void)
{
BUS_READ(oldpc);
pc = oldpc + ra;
if ((oldpc & 0xFF00) == (pc & 0xFF00))
{
BranchTakenMaskingInterrupt = true;
addressModeCycleFn = &M6502::InstructionFetch; // Opcode has already been executed in T1 so just move on to the next instruction.
}
else
{
addressModeCycleFn = &M6502::rel_5_8_T3;
}
}
// When executing a BRK and an interrupt condition is triggered between T0 and T4 the BRK morphs into the interrupt instruction.
// We check here if we continue on executing the BRK or morph and take the interrupt.
void M6502::brk_5_4_T4(void)
{
#ifdef SUPPORT_NMI
if (NMIPending)
{
NMIPending = 0;
NMI_T4();
return;
}
#endif
#ifdef SUPPORT_IRQ
if (IRQPending && !IRQDisabled())
{
IRQPending = 0;
IRQ_T4();
return;
}
#endif
Push(status | FLAG_CONSTANT | FLAG_BREAK);
addressModeCycleFn = &M6502::brk_5_4_T5;
}
// It is possible for a BRK/IRQ to mask a NMI for short burts of NMI assertions.
// If the NMI asserts and un-asserts between IRQ_T4 and IRQ_T6 this will occur.
// This occurs on real hardware and it will be emulated correctly.
// The processor has already commited to fetching the interrupt vectors and will now complete this process.
// If the NMI now un-asserts between now and the end of IRQ_T6 it will be missed/masked.
// The ability for a BRK/IRQ to turn into a NMI shows how the designers of the 6502 anticipated this masking and kept it to a minimum of only four 1/2 cycles!
// But then again, perhpas not, as a NMI that asserts after IRQ_T4 and remains asserted will not be processed until the first instruction of the IRQ routine has completed (see InstructionFetchIRQ).
void M6502::IRQ_T4(void)
{
#ifdef SUPPORT_NMI
if (NMIPending)
{
NMIPending = 0;
NMI_T4();
return;
}
#endif
ClearB();
Push(status);
addressModeCycleFn = &M6502::IRQ_T5;
}
// Interrupts are polled before starting a new instruction
// T0 of every address mode (except reset).
void M6502::InstructionFetch()
{
opcode = BUS_READ(pc); // Technically the InstructionFetch cycle T0 is part of the previous instruction's execution and the check for interrupts occurs after this fetch.
#ifdef SUPPORT_NMI
if (NMIPending)
addressModeCycleFn = &M6502::NMI_T1;
else
#endif // SUPPORT_NMI
#ifdef SUPPORT_IRQ
if (IRQPending && !IRQDisabled())
{
IRQPending = 0;
addressModeCycleFn = &M6502::IRQ_T1;
}
else
#endif // SUPPORT_IRQ
{
pc++;
addressModeCycleFn = T1AddressModeFunctions[opcode];
opcodeCycleFn = opcodeFunctions[opcode];
}
}
#ifdef SUPPORT_IRQ
// If a NMI asserts too late during the IRQ execution ie after IRQ_T4 then it must wait one more instruction. So no polling is performed during this fetch.
// This is an idiosyncrasy of the real hardware and will be emulated using this fuction.
void M6502::InstructionFetchIRQ()
{
opcode = BUS_READ(pc++); // T0
addressModeCycleFn = T1AddressModeFunctions[opcode];
opcodeCycleFn = opcodeFunctions[opcode];
}
#endif
// A single step emulates both real 6502 1/2 cycles.
// On a real 6502, interrupts can be asserted between 1/2 cycles. When this occurs the hardware effectively ignores it for a further 1/2 cycle anyway.
// Here, interrupts are polled at the start of a cycle (in an instruction fetch cycle) emulating this behaviour.
// High frequency (1/2 cycle) bursts of interrupts assertions and un-assertions occuring mid full cycle will be missed by the hardware anyway.
void M6502::Step(void)
{
bool irq;
// If an IRQ occurs during a CLI then it will not take effect until the instruction after the CLI has been executed.
// To emulate this, CLI simply sets CLIMaskingInterrupt flag.
// Similar behaviour can be witnessed with the 3 cycle branch taken instruction.
#ifdef SUPPORT_IRQ
irq = IRQ.IsAsserted();
if (irq && ((status & FLAG_INTERRUPT) == 0) && !CLIMaskingInterrupt && !BranchTakenMaskingInterrupt)
IRQPending = 1;
if (!irq)
IRQPending = 0;
#endif // SUPPORT_IRQ
#ifdef SUPPORT_NMI
NMIPending = NMI.IsAsserted() && !CLIMaskingInterrupt && !BranchTakenMaskingInterrupt;
#endif // SUPPORT_NMI
if (CLIMaskingInterrupt) // If so we have delayed the IRQ long enough for the next instruction to now start. The CLI will then take effect after that instruction completes executing.
CLIMaskingInterrupt = false;
if (BranchTakenMaskingInterrupt) // If so we have delayed the IRQ long enough for the next instruction to now start.
BranchTakenMaskingInterrupt = false;
#ifdef SUPPORT_RDY_HALTING
if (!Halted())
{
CheckForHalt();
(this->*M6502::addressModeCycleFn)();
}
#else
(this->*M6502::addressModeCycleFn)();
#endif // SUPPORT_RDY_HALTING
}
void M6502::Reset(void)
{
CLIMaskingInterrupt = false;
BranchTakenMaskingInterrupt = false;
#ifdef SUPPORT_IRQ
IRQ.Reset();
IRQPending = 0;
#endif // SUPPORT_IRQ
#ifdef SUPPORT_NMI
NMI.Reset();
NMIPending = 0;
#endif // SUPPORT_NMI
#ifdef SUPPORT_RDY_HALTING
RDYCounter = 0; // Don't know if the real hardware does this.
RDYAsserted = 0;
RDYHalted = 0;
#endif // SUPPORT_RDY_HALTING
Reset_T0();
}
#ifdef SUPPORT_RDY_HALTING
void M6502::RDY(bool asserted)
{
if (asserted && (RDYHalted == 0)) RDYCounter = 3;
RDYAsserted = asserted;
if (RDYHalted && !asserted) RDYHalted = 0;
}
void M6502::CheckForHalt()
{
if ((RDYHalted == 0) && RDYAsserted && RDYCounter)
{
RDYCounter--;
if (RDYCounter == 0) RDYHalted = 1;
}
}
u8 M6502::BusRead(u16 address)
{
if ((RDYHalted == 0) && RDYAsserted) RDYHalted = 1;
return dataBusReadFn(address);
}
#endif // SUPPORT_RDY_HALTING

537
m6502.h Normal file
View File

@ -0,0 +1,537 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////
// Emulates a NMOS 6502
//
// To keep the hardware design simple the 6502 performs a bus read or a write on every cycle no matter what it is doing (including resetting).
// Some of these reads are discarded and not used by the CPU (even re-read later). But these bus accesses may impact other devices on the bus (eg write only memory mapped hardware registers of other devices).
// This emulator tries to emulate the CPU to a cycle accurate level including the every cycle bus accesses.
//
// The undocumented instructions and the undocumented address modes are fully emulated.
//
// Instructions in a 6502 execute over muliple cycles anywhere between 2 to 8 cycles.
// This is caused by various address modes requiring multiple cycles to process (often requiring multiple bus accesses) before the opcode can be executed.
// Typically, after all address mode functions/cycles complete the opcode function will follow.
// The branch instructions are an anomaly to this sequence in that after their opcode executes subsequent address mode cycles may follow.
// (This is more than likely the reason why the real 6502 exhibits idiosyncrasies with branch taken and IRQ/NMI assertions (Idiosyncrasies that are also emulated here)).
//
// This emulator breaks up instructions into the correct sequence of functions which are called one after another, each taking a cycle. (Just like the real hardware's state machine)
//
// To use
// You need to supply bus read and write functions. These take the form;-
// u8 read(u16 address)
// void write(u16 address, u8 value)
// Note: external code is responsible for mapping devices into the address space.
// In a 6502 system if a device is not mapped to an address range the 6502 will read back the last value placed onto the data bus (eg the high byte of the address)
// Your external bus read fuction will need to implement this for correct emulation.
//
// CPU can then be supplied with input signals;-
// CLOCK - call Step().
// RESET - can be asserted by calling Reset().
// RDY - assertion state can be set via RDY(bool asserted) function.
// SO - can be asserted by calling SO().
//
// IRQ and NMI interrupts can be asserted via the "Interrupt" support class and calling its Assert()/Release() functions on the emulated CPU's child objects;-
// IRQ
// NMI
//
// Output signals emulated;-
// SYNC - can be polled by calling SYNC()
//
// You can also read the internal state of the processor via;-
// GetRegs - returns a copy of all major CPU registers
// IRQDisabled - returns the status of the I flag in the CPU's status register
#ifndef M6502_H
#define M6502_H
#include "types.h"
// Turn SUPPORT_RDY_HALTING on if you would like to support the RDY line and halting the CPU. (eg BA from the VIC-II in a C64)
//#define SUPPORT_RDY_HALTING
#ifdef SUPPORT_RDY_HALTING
#define BUS_READ BusRead
#else
#define BUS_READ dataBusReadFn
#endif // SUPPORT_RDY_HALTING
//#define SUPPORT_NMI // Some devices don't use the NMI eg Commodore 1541
#define SUPPORT_IRQ // Some devices don't use IRQ eg Atari 7800
// Visual6502 explains the XAA_MAGIC value (http://visual6502.org/wiki/index.php?title=6502_Opcode_8B_(XAA,_ANE)
// From taking measurements from my 1541 drives, they all use EE.
#define XAA_MAGIC 0xee
#define LXA_MAGIC 0xee
//2, 3 or 4 cycles
#define BRANCH_CONDITION(flag, condition) \
ra = BUS_READ(pc++); \
if (ra & 0x80) ra |= 0xFF00; \
if ((status & flag) == condition) \
{ \
oldpc = pc; \
pc = (pc & 0xff00) | ((pc + ra) & 0xff);\
addressModeCycleFn = &M6502::rel_5_8_T2;\
} \
else addressModeCycleFn = &M6502::InstructionFetch;
typedef u8(*DataBusReadFn)(u16 address);
typedef void(*DataBusWriteFn)(u16 address, const u8 value);
#if defined(SUPPORT_IRQ) || defined(SUPPORT_NMI)
class Interrupt
{
public:
Interrupt() : asserted(false) { }
inline bool IsAsserted() { return asserted; }
inline void Assert() { asserted = true; }
inline void Release() { asserted = false; }
inline void Reset() { Release(); }
private:
bool asserted;
};
#endif // SUPPORT_IRQ
class M6502
{
private:
enum
{
FLAG_CARRY = 0x01,
FLAG_ZERO = 0x02,
FLAG_INTERRUPT = 0x04,
FLAG_DECIMAL = 0x08,
FLAG_BREAK = 0x10,
FLAG_CONSTANT = 0x20,
FLAG_OVERFLOW = 0x40,
FLAG_SIGN = 0x80
};
typedef void (M6502::*AddressModeCycleFunction)(void); // Member function pointers for the starting cycle of the address mode functions.
static AddressModeCycleFunction T1AddressModeFunctions[256];
typedef void (M6502::*OpcodeCycleFunction)(void); // Member function pointers for the opcodes.
static OpcodeCycleFunction opcodeFunctions[256];
union
{
u16 ea; // Effective address
u16 ra; // Realitive address
};
union
{
u16 ia; // Intermediate address
u16 oldpc; // A branch's old PC
};
u16 value; // Intermediate data value
u16 pc; // Program Counter
u8 opcode; // The current Opcode
u8 a, x, y, status, sp; // Registers
// Idiosyncrasies of CLI and the 3 cycle Branch Taken instructions can delay interrupts if an interrupt asserts during the execution of those instructions.
// These flags allow this behaviour to be emulated correctly.
u8 CLIMaskingInterrupt : 1;
u8 BranchTakenMaskingInterrupt : 1;
// Flags for tracking interrupts
#ifdef SUPPORT_IRQ
u8 IRQPending : 1;
#endif // SUPPORT_IRQ
#ifdef SUPPORT_NMI
u8 NMIPending : 1;
#endif // SUPPORT_NMI
// Flags for tracking the state of RDY
#ifdef SUPPORT_RDY_HALTING
u8 RDYCounter : 4;
u8 RDYAsserted : 1;
u8 RDYHalted : 1;
#endif // SUPPORT_RDY_HALTING
DataBusReadFn dataBusReadFn; // A pointer to the externally supplied Data Bus read function.
DataBusWriteFn dataBusWriteFn; // A pointer to the externally supplied Data Bus write function.
AddressModeCycleFunction addressModeCycleFn; // Our pointer to the function that will process the current address mode functionality for the current cycle.
OpcodeCycleFunction opcodeCycleFn; // Our pointer to the function that will be called after (or during) the address mode cycle(s) that execute the actual opcode.
inline void ExecuteOpcode(void) { (this->*M6502::opcodeCycleFn)(); addressModeCycleFn = &M6502::InstructionFetch; } // Helper function to call opcodeCycleFn and set up for the next instruction fetch.
// Stack manipulation helpers.
inline void Push(u8 val) { dataBusWriteFn(0x100 + sp--, val); }
inline u8 Pull(void) { return (dataBusReadFn(0x100 + ++sp)); }
// Helper function to write back the results of an instruction (to memory or the A register).
inline void WriteValue(u8 byte)
{
if (addressModeCycleFn == &M6502::sb_1_T1) a = byte;
else dataBusWriteFn(ea, byte);
}
void InstructionFetch(); // T0 of every address mode (except reset).
#ifdef SUPPORT_IRQ
void InstructionFetchIRQ(); // Special case to correctly emulate an IRQ masking a NMI.
#endif
// Opcode functions for documented instructions.
void ADC(void);
void ANC(void) { u16 result = a & value; EstablishNZ(result); SetC(result & 0x0080); a = (u8)result; }
void AND(void) { u16 result = a & value; EstablishNZ(result); a = (u8)result; }
void ASL(void) { u16 result = value << 1; EstablishC(result); EstablishNZ(result); WriteValue(result); }
void BCC(void) { BRANCH_CONDITION(FLAG_CARRY, 0); }
void BCS(void) { BRANCH_CONDITION(FLAG_CARRY, FLAG_CARRY); }
void BEQ(void) { BRANCH_CONDITION(FLAG_ZERO, FLAG_ZERO); }
void BIT(void) { u16 result = a & value; EstablishZ(result); SetV(value & 0x40); EstablishN(value); }
void BMI(void) { BRANCH_CONDITION(FLAG_SIGN, FLAG_SIGN); }
void BNE(void) { BRANCH_CONDITION(FLAG_ZERO, 0); }
void BPL(void) { BRANCH_CONDITION(FLAG_SIGN, 0); }
void BVC(void) { BRANCH_CONDITION(FLAG_OVERFLOW, 0); }
void BVS(void) { BRANCH_CONDITION(FLAG_OVERFLOW, FLAG_OVERFLOW); }
void BRK(void) {}
void CLC(void) { ClearC(); }
void CLD(void) { ClearD(); }
void CLI(void) { ClearI(); CLIMaskingInterrupt = true; } // Like the real hardware the flag will be cleared here (incase it is read by the next instruction) but needs to dely one more cycle (and let another instruction execute) before the IRQ handling will possibly trigger (CLIMaskingInterrupt is used to track and emulate this).
void CLV(void) { ClearV(); }
void CMP(void) { u16 result = a - value; SetC(a >= (u8)value); SetZ(a == (u8)value); EstablishN(result); }
void CPX(void) { u16 result = x - value; SetC(x >= (u8)value); SetZ(x == (u8)value); EstablishN(result); }
void CPY(void) { u16 result = y - value; SetC(y >= (u8)value); SetZ(y == (u8)value); EstablishN(result); }
void DEC(void) { u16 result = value - 1; EstablishNZ(result); WriteValue(result); }
void DEX(void) { x--; EstablishNZ(x); }
void DEY(void) { y--; EstablishNZ(y); }
void EOR(void) { u16 result = a ^ value; EstablishNZ(result); a = (u8)result; }
void INC(void) { u16 result = value + 1; EstablishNZ(result); WriteValue(result); }
void INX(void) { x++; EstablishNZ(x); }
void INY(void) { y++; EstablishNZ(y); }
void JAM(void) {}
void JMP(void) { pc = ea; }
void JSR(void) {}
void LDA(void) { a = (u8)value; EstablishNZ(a); }
void LDX(void) { x = (u8)value; EstablishNZ(x); }
void LDY(void) { y = (u8)value; EstablishNZ(y); }
void LSR(void) { u16 result = value >> 1; SetC(value & 1); EstablishNZ(result); WriteValue(result); }
void NOP(void) {}
void ORA(void) { u16 result = a | value; EstablishNZ(result); a = (u8)result; }
void PHA(void) { Push(a);}
void PHP(void) { Push(status | FLAG_CONSTANT | FLAG_BREAK); } // PHP always pushes the Break (B) flag as a `1' to the stack. Needs to push SO status into V also? (ie what cycle of PHP can SO assertions take effect?)
void PLA(void) { a = Pull(); EstablishNZ(a); }
void PLP(void) { status = Pull() | FLAG_CONSTANT; }
void ROL(void) { u16 result = (value << 1) | (status & FLAG_CARRY); EstablishC(result); EstablishNZ(result); WriteValue(result); }
void ROR(void) { u16 result = (value >> 1) | ((status & FLAG_CARRY) << 7); SetC(value & 1); EstablishNZ(result); WriteValue(result); } // Post June, 1976 version.
void RTI(void) {}
void RTS(void) {}
void SBC(void);
void SEC(void) { SetC(); }
void SED(void) { SetD(); }
void SEI(void) { SetI(); }
void STA(void) { WriteValue(a); }
void STX(void) { WriteValue(x); }
void STY(void) { WriteValue(y); }
void TAX(void) { x = a; EstablishNZ(a); }
void TAY(void) { y = a; EstablishNZ(y); }
void TSX(void) { x = sp; EstablishNZ(x); }
void TXA(void) { a = x; EstablishNZ(a); }
void TXS(void) { sp = x; }
void TYA(void) { a = y; EstablishNZ(a); }
// Opcode functions for undocumented instructions.
void ASR(void) { u16 result = a & value; SetC(result & 1); result >>= 1; EstablishNZ(result); a = (u8)result; }
void LXA(void) { u16 result = (a | LXA_MAGIC) & value; EstablishNZ(result); x = (u8)result; a = x; }
void ARR(void);
void LAX(void) { LDA(); LDX(); }
void LAS(void) { sp = sp & (u8)value; x = sp; a = sp; EstablishNZ(x); }
void SAX(void) { WriteValue(a & x); }
void SBX(void) { u16 result = (a & x) - value; x = result & 0xff; EstablishNZ(x); SetC(result < 0x100); }
void SHA(void) { WriteValue(a & x & ((ea >> 8) + 1)); }
void SHY(void) { u16 result = ((ea >> 8) + 1) & y; WriteValue(result); }
void DCP(void) { u16 result = (value - 1) & 0xff; SetC(a >= (u8)result); EstablishNZ(a - (u8)result); WriteValue(result); }
void ISB(void) { value = (value + 1) & 0xff; WriteValue(value); SBC(); }
void SLO(void) { u16 result = value << 1; EstablishC(result); a |= (u8)result; EstablishNZ(a); WriteValue(result); }
void RLA(void) { u16 result = (value << 1) | (status & FLAG_CARRY); EstablishC(result); a &= (u8)result; EstablishNZ(a); WriteValue(result); }
void SRE(void) { u16 result = value >> 1; SetC(value & 1); a ^= (u8)result; EstablishNZ(a); WriteValue(result); }
void RRA(void) { u16 result = (value >> 1) | ((status & FLAG_CARRY) << 7); SetC(value & 1); WriteValue(result); value = result & 0xff; ADC(); } // The ADC will use the carry we set here
void SHS(void) { u16 result = (a & x); WriteValue(result & ((ea >> 8) + 1)); sp = (u8)result; }
void SHX(void) { u16 result = ((ea >> 8) + 1) & x; WriteValue(result); }
void XAA(void) { u16 result = ((a | XAA_MAGIC) & x & ((u8)(value))); EstablishNZ(result); a = (u8)result; }
// For address modes, timings and stages were taken from the "MCS6500 Family Hardware Manual"
// Numbers in the fuction name are from apendix A section on the manual.
// eg idy_3_6_T3 is Indirect Y Addressing Mode detailed in section 3.6 of the manual's appendix A.
// T3 means the T3 stage explained in the manual.
// Single byte instructions
void sb_1_T1(void) { BUS_READ(pc); value = a; ExecuteOpcode(); } //2 cycles
void sb_jam_T1(void) { BUS_READ(pc); ExecuteOpcode(); } //2 cycles
void imm_2_1_T1(void) { value = BUS_READ(pc++); ExecuteOpcode(); } //2 cycles
void rel_5_8_T1(void) { (this->*M6502::opcodeCycleFn)(); } // Branch instructions are the anomaly and execute their opcode in T1.
void rel_5_8_T2(void);
void rel_5_8_T3(void) { BUS_READ(pc); addressModeCycleFn = &M6502::InstructionFetch; } // Opcode has already been executed in T1 so just move on to the next instruction.
void zp_2_1_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::zp_2_1_T2; } //3 cycles
void zp_2_1_T2(void) { value = BUS_READ(ea); ExecuteOpcode(); }
void zp_3_1_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::zp_3_1_T2; } //3 cycles
void zp_3_1_T2(void) { ExecuteOpcode(); }
void abs_2_3_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::abs_2_3_T2; } //4 cycles
void abs_2_3_T2(void) { ea |= (BUS_READ(pc++) << 8); addressModeCycleFn = &M6502::abs_2_3_T3; }
void abs_2_3_T3(void) { value = BUS_READ(ea); ExecuteOpcode(); }
void abs_3_2_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::abs_3_2_T2; } //4 cycles
void abs_3_2_T2(void) { ea |= (BUS_READ(pc++) << 8); addressModeCycleFn = &M6502::abs_3_2_T3; }
void abs_3_2_T3(void) { ExecuteOpcode(); }
void idx_2_4_T1(void) { ia = BUS_READ(pc++); addressModeCycleFn = &M6502::idx_2_4_T2; } //6 cycles
void idx_2_4_T2(void) { BUS_READ(ia); addressModeCycleFn = &M6502::idx_2_4_T3; }
void idx_2_4_T3(void) { ia = (ia + x) & 0xff; ea = BUS_READ(ia++); addressModeCycleFn = &M6502::idx_2_4_T4; }
void idx_2_4_T4(void) { ea |= (BUS_READ(ia & 0xff) << 8); addressModeCycleFn = &M6502::idx_2_4_T5; }
void idx_2_4_T5(void) { value = BUS_READ(ea); ExecuteOpcode(); }
void idx_3_3_T1(void) { ia = BUS_READ(pc++); addressModeCycleFn = &M6502::idx_3_3_T2; } //6 cycles
void idx_3_3_T2(void) { BUS_READ(ia); addressModeCycleFn = &M6502::idx_3_3_T3; }
void idx_3_3_T3(void) { ia = (ia + x) & 0xff; ea = BUS_READ(ia++); addressModeCycleFn = &M6502::idx_3_3_T4; }
void idx_3_3_T4(void) { ea |= (BUS_READ(ia & 0xff) << 8); addressModeCycleFn = &M6502::idx_3_3_T5; }
void idx_3_3_T5(void) { ExecuteOpcode(); }
// idx_Undoc behaviour was determined by capturing bus activity on a real 6502 in a 1541 and confirmed by observing Visual6502.
void idx_Undoc_T1(void) { ia = BUS_READ(pc++); addressModeCycleFn = &M6502::idx_Undoc_T2; } //8 cycles
void idx_Undoc_T2(void) { BUS_READ(ia); addressModeCycleFn = &M6502::idx_Undoc_T3; }
void idx_Undoc_T3(void) { ia = (ia + x) & 0xff; ea = BUS_READ(ia++); addressModeCycleFn = &M6502::idx_Undoc_T4; }
void idx_Undoc_T4(void) { ea |= (BUS_READ(ia & 0xff) << 8); addressModeCycleFn = &M6502::idx_Undoc_T5; }
void idx_Undoc_T5(void) { value = BUS_READ(ea); addressModeCycleFn = &M6502::idx_Undoc_T6; }
void idx_Undoc_T6(void) { dataBusWriteFn(ea, (u8)value); addressModeCycleFn = &M6502::idx_Undoc_T7; }
void idx_Undoc_T7(void) { ExecuteOpcode(); }
void absx_2_5_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::absx_2_5_T2; } //4/5 cycles
void absx_2_5_T2(void) { ea |= (BUS_READ(pc++) << 8); addressModeCycleFn = &M6502::absx_2_5_T3; }
void absx_2_5_T3(void);
void absx_2_5_T4(void) { value = BUS_READ(ea); ExecuteOpcode(); }
void absx_3_4_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::absx_3_4_T2; } //5 cycles
void absx_3_4_T2(void) { ea |= (BUS_READ(pc++) << 8); addressModeCycleFn = &M6502::absx_3_4_T3; }
void absx_3_4_T3(void) { BUS_READ(ea); ea += x; addressModeCycleFn = &M6502::absx_3_4_T4; }
void absx_3_4_T4(void) { ExecuteOpcode(); }
void absy_2_5_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::absy_2_5_T2; } //4/5 cycles
void absy_2_5_T2(void) { ea |= (BUS_READ(pc++) << 8); addressModeCycleFn = &M6502::absy_2_5_T3; }
void absy_2_5_T3(void);
void absy_2_5_T4(void) { value = BUS_READ(ea); ExecuteOpcode(); }
void absy_3_4_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::absy_3_4_T2; } //5 cycles
void absy_3_4_T2(void) { ea |= (BUS_READ(pc++) << 8); addressModeCycleFn = &M6502::absy_3_4_T3; }
void absy_3_4_T3(void) { BUS_READ(ea); ea += y; addressModeCycleFn = &M6502::absy_3_4_T4; }
void absy_3_4_T4(void) { ExecuteOpcode(); }
void zpx_2_6_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::zpx_2_6_T2; } //4 cycles
void zpx_2_6_T2(void) { BUS_READ(ea); addressModeCycleFn = &M6502::zpx_2_6_T3; }
void zpx_2_6_T3(void) { ea = (ea + x) & 0xFF; value = BUS_READ(ea); ExecuteOpcode(); }
void zpx_3_5_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::zpx_3_5_T2; } //4 cycles
void zpx_3_5_T2(void) { BUS_READ(ea); addressModeCycleFn = &M6502::zpx_3_5_T3; }
void zpx_3_5_T3(void) { ea = (ea + x) & 0xFF; ExecuteOpcode(); }
void zpy_2_6_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::zpy_2_6_T2; } //4 cycles
void zpy_2_6_T2(void) { BUS_READ(ea); addressModeCycleFn = &M6502::zpy_2_6_T3; }
void zpy_2_6_T3(void) { ea = (ea + y) & 0xFF; value = BUS_READ(ea); ExecuteOpcode(); }
void zpy_3_5_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::zpy_3_5_T2; } //4 cycles
void zpy_3_5_T2(void) { BUS_READ(ea); addressModeCycleFn = &M6502::zpy_3_5_T3; }
void zpy_3_5_T3(void) { ea = (ea + y) & 0xFF; ExecuteOpcode(); }
void idy_2_7_T1(void) { ia = BUS_READ(pc++); addressModeCycleFn = &M6502::idy_2_7_T2; } //5/6 cycles
void idy_2_7_T2(void) { ea = BUS_READ(ia++); addressModeCycleFn = &M6502::idy_2_7_T3; }
void idy_2_7_T3(void) { ea |= (BUS_READ(ia & 0xff) << 8); addressModeCycleFn = &M6502::idy_2_7_T4; }
void idy_2_7_T4(void);
void idy_2_7_T5(void) { value = BUS_READ(ea); ExecuteOpcode(); }
void idy_3_6_T1(void) { ia = BUS_READ(pc++); addressModeCycleFn = &M6502::idy_3_6_T2; } //6 cycles
void idy_3_6_T2(void) { ea = BUS_READ(ia++); addressModeCycleFn = &M6502::idy_3_6_T3; }
void idy_3_6_T3(void) { ea |= (BUS_READ(ia & 0xff) << 8); addressModeCycleFn = &M6502::idy_3_6_T4; }
void idy_3_6_T4(void) { ea += y; BUS_READ(ea); addressModeCycleFn = &M6502::idy_3_6_T5; }
void idy_3_6_T5(void) { ExecuteOpcode(); }
// idy_Undoc behaviour was determined by capturing bus activity on a real 6502 in a 1541 and confirmed by Visual6502.
void idy_Undoc_T1(void) { ia = BUS_READ(pc++); addressModeCycleFn = &M6502::idy_Undoc_T2; } //8 cycles
void idy_Undoc_T2(void) { ea = BUS_READ(ia++); addressModeCycleFn = &M6502::idy_Undoc_T3; }
void idy_Undoc_T3(void) { ea |= (BUS_READ(ia & 0xff) << 8); addressModeCycleFn = &M6502::idy_Undoc_T4; }
void idy_Undoc_T4(void) { ea += y; BUS_READ(ea); addressModeCycleFn = &M6502::idy_Undoc_T5; }
void idy_Undoc_T5(void) { value = BUS_READ(ea); addressModeCycleFn = &M6502::idy_Undoc_T6; }
void idy_Undoc_T6(void) { dataBusWriteFn(ea, (u8)value); addressModeCycleFn = &M6502::idy_Undoc_T7; }
void idy_Undoc_T7(void) { ExecuteOpcode(); }
void zp_4_1_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::zp_4_1_T2; } //5 cycles
void zp_4_1_T2(void) { value = BUS_READ(ea); addressModeCycleFn = &M6502::zp_4_1_T3; }
void zp_4_1_T3(void) { dataBusWriteFn(ea, (u8)value); addressModeCycleFn = &M6502::zp_4_1_T4; }
void zp_4_1_T4(void) { ExecuteOpcode(); }
void abs_4_2_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::abs_4_2_T2; } //6 cycles
void abs_4_2_T2(void) { ea |= (BUS_READ(pc++) << 8); addressModeCycleFn = &M6502::abs_4_2_T3; }
void abs_4_2_T3(void) { value = BUS_READ(ea); addressModeCycleFn = &M6502::abs_4_2_T4; }
void abs_4_2_T4(void) { dataBusWriteFn(ea, (u8)value); addressModeCycleFn = &M6502::abs_4_2_T5; }
void abs_4_2_T5(void) { ExecuteOpcode(); }
void zpx_4_3_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::zpx_4_3_T2; } //6 cycles
void zpx_4_3_T2(void) { BUS_READ(ea); addressModeCycleFn = &M6502::zpx_4_3_T3; }
void zpx_4_3_T3(void) { ea = (ea + x) & 0xFF; value = BUS_READ(ea); addressModeCycleFn = &M6502::zpx_4_3_T4; }
void zpx_4_3_T4(void) { dataBusWriteFn(ea, (u8)value); addressModeCycleFn = &M6502::zpx_4_3_T5; }
void zpx_4_3_T5(void) { ExecuteOpcode(); }
void absx_4_4_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::absx_4_4_T2; } //7 cycles
void absx_4_4_T2(void) { ea |= (BUS_READ(pc++) << 8); addressModeCycleFn = &M6502::absx_4_4_T3; }
void absx_4_4_T3(void) { ea += x; BUS_READ(ea); addressModeCycleFn = &M6502::absx_4_4_T4; }
void absx_4_4_T4(void) { value = BUS_READ(ea); addressModeCycleFn = &M6502::absx_4_4_T5; }
void absx_4_4_T5(void) { dataBusWriteFn(ea, (u8)value); addressModeCycleFn = &M6502::absx_4_4_T6; }
void absx_4_4_T6(void) { ExecuteOpcode(); }
void absy_4_4_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::absy_4_4_T2; } //7 cycles
void absy_4_4_T2(void) { ea |= (BUS_READ(pc++) << 8); addressModeCycleFn = &M6502::absy_4_4_T3; }
void absy_4_4_T3(void) { ea += y; BUS_READ(ea); addressModeCycleFn = &M6502::absy_4_4_T4; }
void absy_4_4_T4(void) { value = BUS_READ(ea); addressModeCycleFn = &M6502::absy_4_4_T5; }
void absy_4_4_T5(void) { dataBusWriteFn(ea, (u8)value); addressModeCycleFn = &M6502::absy_4_4_T6; }
void absy_4_4_T6(void) { ExecuteOpcode(); }
void ph_5_1_T1(void) { BUS_READ(pc); addressModeCycleFn = &M6502::ph_5_1_T2; } //3 cycles
void ph_5_1_T2(void) { ExecuteOpcode(); }
void pl_5_2_T1(void) { BUS_READ(pc); addressModeCycleFn = &M6502::pl_5_2_T2; } //4 cycles
void pl_5_2_T2(void) { BUS_READ(0x100 + sp); addressModeCycleFn = &M6502::pl_5_2_T3; }
void pl_5_2_T3(void) { ExecuteOpcode(); }
void jsr_5_3_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::jsr_5_3_T2; } //6 cycles
void jsr_5_3_T2(void) { BUS_READ(0x100 + sp); addressModeCycleFn = &M6502::jsr_5_3_T3; }
void jsr_5_3_T3(void) { Push((u8)((pc) >> 8)); addressModeCycleFn = &M6502::jsr_5_3_T4; }
void jsr_5_3_T4(void) { Push(pc & 0xff); addressModeCycleFn = &M6502::jsr_5_3_T5; }
void jsr_5_3_T5(void) { ea |= (BUS_READ(pc++) << 8); pc = ea; ExecuteOpcode(); }
void rti_5_5_T1(void) { BUS_READ(pc++); addressModeCycleFn = &M6502::rti_5_5_T2; } //6 cycles
void rti_5_5_T2(void) { BUS_READ(0x100 + sp); addressModeCycleFn = &M6502::rti_5_5_T3; }
void rti_5_5_T3(void) { status = Pull(); addressModeCycleFn = &M6502::rti_5_5_T4; }
void rti_5_5_T4(void) { pc = Pull(); addressModeCycleFn = &M6502::rti_5_5_T5; }
void rti_5_5_T5(void) { pc |= (Pull() << 8); ExecuteOpcode(); }
void abs5_6_1_T1(void) { ea = BUS_READ(pc++); addressModeCycleFn = &M6502::abs5_6_1_T2; } //3 cycles
void abs5_6_1_T2(void) { ea |= (BUS_READ(pc++) << 8); ExecuteOpcode(); }
void abs5_6_2_T1(void) { ia = BUS_READ(pc++); addressModeCycleFn = &M6502::abs5_6_2_T2; } //5 cycles
void abs5_6_2_T2(void) { ia |= (BUS_READ(pc++) << 8); addressModeCycleFn = &M6502::abs5_6_2_T3; }
void abs5_6_2_T3(void) { ea = BUS_READ(ia++); addressModeCycleFn = &M6502::abs5_6_2_T4; }
void abs5_6_2_T4(void) { ea |= (BUS_READ(ia) << 8); ExecuteOpcode(); }
void rts_5_7_T1(void) { BUS_READ(pc++); addressModeCycleFn = &M6502::rts_5_7_T2; } //6 cycles
void rts_5_7_T2(void) { BUS_READ(0x100 + sp); addressModeCycleFn = &M6502::rts_5_7_T3; }
void rts_5_7_T3(void) { pc = Pull(); addressModeCycleFn = &M6502::rts_5_7_T4; }
void rts_5_7_T4(void) { pc |= (Pull() << 8); addressModeCycleFn = &M6502::rts_5_7_T5; }
void rts_5_7_T5(void) { BUS_READ(pc); pc++; ExecuteOpcode(); }
// The BRK, RESET, NMI and IRQ instructions are closely related.
// At T4 BRK can morph into one of the interrupts if that interrupt condition has subsequently occurred since the instruction started.
void brk_5_4_T1(void) { BUS_READ(pc); pc++; addressModeCycleFn = &M6502::brk_5_4_T2; } //7 cycles
void brk_5_4_T2(void) { Push((u8)(pc >> 8)); addressModeCycleFn = &M6502::brk_5_4_T3; }
void brk_5_4_T3(void) { Push(pc & 0xff); addressModeCycleFn = &M6502::brk_5_4_T4; }
void brk_5_4_T4(void); // We check here if we continue on executing the BRK or take the interrupt.
void brk_5_4_T5(void) { ea = BUS_READ(0xFFFE); addressModeCycleFn = &M6502::brk_5_4_T6; } // Short burts of interrupt assertions will be correctly masked by the BRK in these 2 cycles.
void brk_5_4_T6(void) { SetI(); pc = ea | (BUS_READ(0xFFFF) << 8); ExecuteOpcode(); }
void Reset_T0(void) { sp = 0; BUS_READ(pc); addressModeCycleFn = &M6502::Reset_T1; } //7 cycles
void Reset_T1(void) { BUS_READ(pc); addressModeCycleFn = &M6502::Reset_T2; }
void Reset_T2(void) { BUS_READ(0x100 + sp--); addressModeCycleFn = &M6502::Reset_T3; }
void Reset_T3(void) { BUS_READ(0x100 + sp--); addressModeCycleFn = &M6502::Reset_T4; }
void Reset_T4(void) { ClearB(); BUS_READ(0x100 + sp--); addressModeCycleFn = &M6502::Reset_T5; }
void Reset_T5(void) { ea = BUS_READ(0xFFFC); addressModeCycleFn = &M6502::Reset_T6; }
void Reset_T6(void) { pc = ea | (BUS_READ(0xFFFD) << 8); addressModeCycleFn = &M6502::InstructionFetch; }
#ifdef SUPPORT_NMI
void NMI_T1(void) { BUS_READ(pc); addressModeCycleFn = &M6502::NMI_T2; } //7 cycles
void NMI_T2(void) { Push((u8)(pc >> 8)); addressModeCycleFn = &M6502::NMI_T3; }
void NMI_T3(void) { Push(pc & 0xff); addressModeCycleFn = &M6502::NMI_T4; }
void NMI_T4(void) { ClearB(); Push(status); status |= FLAG_INTERRUPT; addressModeCycleFn = &M6502::NMI_T5; }
void NMI_T5(void) { ea = BUS_READ(0xFFFA); addressModeCycleFn = &M6502::NMI_T6; }
void NMI_T6(void) { SetI(); pc = ea | (BUS_READ(0xFFFB) << 8); NMIPending = false; addressModeCycleFn = &M6502::InstructionFetch; }
#endif // SUPPORT_NMI
#ifdef SUPPORT_IRQ
void IRQ_T1(void) { BUS_READ(pc); addressModeCycleFn = &M6502::IRQ_T2; } //7 cycles
void IRQ_T2(void) { Push((u8)(pc >> 8)); addressModeCycleFn = &M6502::IRQ_T3; }
void IRQ_T3(void) { Push(pc & 0xff); addressModeCycleFn = &M6502::IRQ_T4; }
void IRQ_T4(void); // We check here if we continue on executing as IRQ or morph into NMI
void IRQ_T5(void) { ea = BUS_READ(0xFFFE); addressModeCycleFn = &M6502::IRQ_T6; } // Short burts of NMI assertions will be correctly masked by the IRQ in these 2 cycles
void IRQ_T6(void) { SetI(); pc = ea | (BUS_READ(0xFFFF) << 8); addressModeCycleFn = &M6502::InstructionFetchIRQ; }
#endif // SUPPORT_IRQ
inline void ClearB() { status &= (~FLAG_BREAK); }
inline void SetB() { status |= FLAG_BREAK; }
inline void ClearC() { status &= (~FLAG_CARRY); }
inline void SetC() { status |= FLAG_CARRY; }
inline void SetC(u16 test) { test != 0 ? SetC() : ClearC(); }
inline void ClearZ() { status &= (~FLAG_ZERO); }
inline void SetZ() { status |= FLAG_ZERO; }
inline void SetZ(u16 test) { test != 0 ? SetZ() : ClearZ(); }
inline void ClearI() {status &= (~FLAG_INTERRUPT); }
inline void SetI() { status |= FLAG_INTERRUPT; }
inline void ClearD() {status &= (~FLAG_DECIMAL); }
inline void SetD() { status |= FLAG_DECIMAL; }
inline void ClearV() {status &= (~FLAG_OVERFLOW); }
inline void SetV() { status |= FLAG_OVERFLOW; }
inline void SetV(u16 test) { test != 0 ? SetV() : ClearV(); }
inline void ClearN() {status &= (~FLAG_SIGN); }
inline void SetN() { status |= FLAG_SIGN; }
inline void SetN(u16 test) { test != 0 ? SetN() : ClearN(); }
inline void EstablishZ(u16 val) { SetZ((val & 0x00FF) == 0); }
inline void EstablishN(u16 val) { SetN(val & 0x0080); }
inline void EstablishC(u16 val) { SetC(val & 0xFF00); }
inline void EstablishV(u16 result, u8 val) { SetV((result ^ a) & (result ^ val) & 0x0080); }
inline void EstablishNZ(u16 val) { EstablishZ(val); EstablishN(val); }
#ifdef SUPPORT_RDY_HALTING
void CheckForHalt();
#endif // SUPPORT_RDY_HALTING
public:
M6502() : status(FLAG_CONSTANT), dataBusReadFn(0), dataBusWriteFn(0) {}
M6502(void* data, DataBusReadFn dataBusReadFn, DataBusWriteFn dataBusWriteFn) { SetBusFunctions(dataBusReadFn, dataBusWriteFn); }
void SetBusFunctions(DataBusReadFn dataBusReadFn, DataBusWriteFn dataBusWriteFn) {this->dataBusReadFn = dataBusReadFn; this->dataBusWriteFn = dataBusWriteFn; status = FLAG_CONSTANT; Reset(); }
void Reset(void);
void Step(void);
#ifdef SUPPORT_RDY_HALTING
void RDY(bool asserted);
bool Halted() { return RDYHalted != 0; }
u8 BusRead(u16 address);
#endif // SUPPORT_RDY_HALTING
inline void SO(void) { SetV(); }
inline bool IRQDisabled(void) const { return (status & FLAG_INTERRUPT) != 0; }
void GetRegs(u16& PC, u8& SP, u8& A, u8& X, u8& Y, u8& Status) { PC = pc; SP = sp; A = a; X = x; Y = y; Status = status; }
u16 GetPC() const { return pc; }
u8 GetA() const { return a; }
u8 GetX() const { return x; }
u8 GetY() const { return y; }
u8 GetStatus() const { return status; }
// Emulate the 6502's SYNC signal and pin
bool SYNC(void) const { return addressModeCycleFn == &M6502::InstructionFetch; }
#ifdef SUPPORT_IRQ
Interrupt IRQ;
#endif // SUPPORT_IRQ
#ifdef SUPPORT_NMI
Interrupt NMI;
#endif // SUPPORT_NMI
};
#endif

719
m6522.cpp Normal file
View File

@ -0,0 +1,719 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#include "m6522.h"
// There are a number of inherent undocumented edge cases with regards to Timer 2.
// A lot of empirical measurements, in the form of bus captures of a real Commodore 1541 VIA were taken to discover the exact behavior of the timers (especially timer 2 and all its idiosyncrasies).
// Many comments in this file are taken from statements found in the 6522 data sheets.
m6522::m6522()
{
Reset();
}
void m6522::Reset()
{
functionControlRegister = 0;
auxiliaryControlRegister = 0;
latchPortA = false;
latchedValueA = 0;
ca1 = false;
ca2 = false;
pulseCA2 = false;
latchedValueB = 0;
cb1 = false;
cb1Old = false;
cb2 = false;
pulseCB2 = false;
t1c.bytes.l = 0xff;
t1c.bytes.h = 0xff;
t1l.bytes.l = 0xff;
t1l.bytes.h = 0xff;
t1Ticking = false;
t1Reload = false;
t1OutPB7 = false;
t1FreeRun = false;
t1_pb7 = true;
t1TimedOut = false;
t1OneShotTriggeredIRQ = false;
t2c.bytes.l = 0xc9; // logic analyser detects that these are some what random
t2c.bytes.h = 0xfb; // logic analyser detects that these are some what random
t2Latch = 0;
t2Reload = false;
t2CountingDown = false;
t2TimedOutCount = 0;
t2LowTimedOut = false;
t2CountingPB6Mode = false;
t2CountingPB6ModeOld = false;
pb6Old = 0;
t2TimedOut = false;
t2OneShotTriggeredIRQ = false;
interruptFlagRegister = 0;
interruptEnabledRegister = 0;
shiftRegister = 0;
// External devices should be doing this
// - what about CA1 and CB1?
InputCA2(true);
InputCB2(true);
bitsShiftedSoFar = 0;
cb1OutputShiftClock = 0;
cb1OutputShiftClockPositiveEdge = false;
cb2Shift = 0;
OutputIRQ();
}
void m6522::InputCA1(bool value)
{
if (ca1 != value && ((functionControlRegister & FCR_CA1) != 0) == value) // CA1 is an input?
{
unsigned char ddr = portA.GetDirection();
latchedValueA = ((portA.GetInput() & ~ddr) | (portA.GetOutput() & ddr));
// test HANDSHAKE OUTPUT mode and if so auto clear
if ((functionControlRegister & (FCR_CA2_IO | FCR_CA2_OUTPUT_MODE1 | FCR_CB2_OUTPUT_MODE0)) == FCR_CA2_IO)
ca2 = false;
SetInterrupt(IR_CA1);
}
ca1 = value;
}
void m6522::InputCA2(bool value)
{
if ((functionControlRegister & FCR_CA2_IO) == 0) // CA2 is an input?
{
if (ca2 != value && ((functionControlRegister & FCR_CA2_EDGE_TRIGGER_MODE) != 0) == value)
SetInterrupt(IR_CA2); // interrupt if we are tracking edges
ca2 = value;
}
}
void m6522::InputCB1(bool value)
{
if (cb1 != value && ((functionControlRegister & FCR_CB1) != 0) == value) // CB1 is an input?
{
unsigned char ddr = portB.GetDirection();
latchedValueB = ((portB.GetInput() & ~ddr) | (portB.GetOutput() & ddr));
// test HANDSHAKE OUTPUT mode and if so auto clear
if ((functionControlRegister & (FCR_CB2_IO | FCR_CB2_OUTPUT_MODE1 | FCR_CB2_OUTPUT_MODE0)) == FCR_CB2_IO)
cb2 = false;
SetInterrupt(IR_CB1);
}
cb1 = value;
}
// If CB2 is not set to an output then reads the CB2 line and stores the value in cb2
void m6522::InputCB2(bool value)
{
if ((functionControlRegister & FCR_CB2_IO) == 0) // CB2 is an input?
{
if (cb2 != value && ((functionControlRegister & FCR_CB2_EDGE_TRIGGER_MODE) != 0) == value)
SetInterrupt(IR_CB2); // interrupt if we are tracking edges
cb2 = value;
}
}
// Update for a single cycle
void m6522::Execute()
{
if (ca2 && pulseCA2) ca2 = false;
if (cb2 && pulseCB2) cb2 = false;
// The t1 counter decrements on each succeeding phi2 from N to 0 and then one half phi2 cycle later IRQ goes active.
// (where N is the combined count value of T1CL and T1CH)
if (t1TimedOut)
{
t1c.value = t1l.value;
t1TimedOut = false;
if (t1FreeRun)
{
if (t1FreeRunIRQsOn)
SetInterrupt(IR_T1);
}
else
{
if (!t1OneShotTriggeredIRQ)
{
t1OneShotTriggeredIRQ = true;
SetInterrupt(IR_T1);
}
// At this time the counter will continue to decrement at system clock rate.
// This allows the system processor to read the contents of the counter to determine the time since interrupt.
}
}
else if (t1Ticking && !t1Reload && !t1c.value--)
{
t1TimedOut = true;
if (t1FreeRun)
{
if (t1l.value > 1) // A real VIA will not flip PB7 if the frequency is above a certain (ie 1 cycle) threshold
{
t1_pb7 = !t1_pb7;
if (t1OutPB7)
{
unsigned char ddr = portB.GetDirection();
if (ddr & 0x80)
{
// the signal on PB7 is inverted each time the counter reaches zero
if (!t1_pb7) portB.SetOutput(portB.GetOutput() & (~0x80));
else portB.SetOutput(portB.GetOutput() | 0x80);
}
}
}
}
else
{
if (!t1OneShotTriggeredIRQ)
{
if (t1OutPB7)
{
// PB7 was set low on the write to T1CH now the signal on PB7 will go high
// The duration of the pulse is equal to N + one and one half (where N equals the count value) to guarantee a valid output level on PB7.
unsigned char ddr = portB.GetDirection();
if (ddr & 0x80) portB.SetOutput(portB.GetOutput() | 0x80);
t1_pb7 = false;
}
}
}
}
t1Reload = false;
// Timer 2 can also be used to count negative pulses on the PB6 line.
unsigned char pb6 = portB.GetInput() & ~portB.GetDirection() & 0x40;
unsigned char shiftMode = (auxiliaryControlRegister & ACR_SHIFTREG_CTRL) >> 2;
// The data is shifted into the shift register during the phi2 clock cycle following the positive going edge of the CB1 clock pulse.
// - So we test the edge of the clock last cycle and if positive shift this cycle.
bool shiftClockPositiveEdge = cb1OutputShiftClockPositiveEdge;
cb1OutputShiftClockPositiveEdge = false;
if (t2TimedOut)
{
t2TimedOut = false;
// In both modes the interrupt is only set once
if (!t2OneShotTriggeredIRQ)
{
t2OneShotTriggeredIRQ = true;
SetInterrupt(IR_T2);
}
else
{
// At this time the counter will continue to decrement at system clock rate or PB6 negative edge counts (depending upon mode)
// This allows the system processor to read the contents of the counter to determine the time since interrupt.
}
if ((auxiliaryControlRegister & 0xc) == 4) // shift by timer 2?
{
cb1OutputShiftClockPositiveEdge = cb1OutputShiftClock; // If positive edge we need to shift next phi2 so cache for one cycle
cb1OutputShiftClock = !cb1OutputShiftClock;
}
if (t2Latch == 0xff)
t2c.value--;
if ((t2TimedOutCount > 1) && t2c.bytes.h == 0)
{
t2c.bytes.h = 0xff;
t2c.bytes.l = t2Latch + 2;
}
else
{
t2c.bytes.l = t2Latch;
}
t2LowTimedOut = false;
}
if (t2CountingDown)
{
if (t2CountingPB6Mode ^ t2CountingPB6ModeOld)
{
// If T2 has changed modes then the IRQ is back on the table
t2OneShotTriggeredIRQ = false;
if (!t2CountingPB6Mode)
{
if (t2c.value == 0) // PB6 mode turned off just as it timed out we still need to interrupt.
SetInterrupt(IR_T2);
}
else
{
// When switching PB6Mode back on it will still count down one more time
t2c.value--;
t2TimedOut = t2c.value == 0;
}
}
else if (!t2Reload) // Only do this if it was not just reloaded by writing to T2CH
{
// Bit 5 of the ACR determines whether the counter is decremented by the 6502 system clock or input pulses arriving on PB6.
if (t2CountingPB6Mode && t2CountingPB6ModeOld)
{
if (pb6 == 0 && pb6Old == 1) // Was it the negative edge?
{
t2c.value--;
t2TimedOut = t2c.value == 0;
}
}
else
{
t2c.value--;
t2TimedOut = t2c.value == 0;
if (t2c.bytes.l == 0xfe)
{
t2TimedOutCount++;
if ((auxiliaryControlRegister & 0xc) == 4) // shift by timer 2?
{
if (t2TimedOutCount > 1)
{
cb1OutputShiftClockPositiveEdge = cb1OutputShiftClock; // If positive edge we need to shift next phi2 so cache for one cycle
cb1OutputShiftClock = !cb1OutputShiftClock;
t2c.bytes.l = t2Latch;
t2TimedOut = false;
}
}
}
}
}
else
{
t2Reload = false;
}
}
pb6Old = pb6;
t2CountingPB6ModeOld = t2CountingPB6Mode;
switch (shiftMode)
{
default: // 000 = shift reg disabled
// The CPU can read and write the SR but shifting is disabled.
// Both CB1 and CB2 are controlled by peripheral control register.
break;
case 1: // 001 = shift in by timer 2
if ((t2TimedOutCount > 2) && shiftClockPositiveEdge && !(bitsShiftedSoFar & 8))
{
// should output cb1OutputShiftClock onto cb1
shiftRegister <<= 1;
shiftRegister |= cb2; // Should get from current cb2 (in a 1541 these pins on the VIAs are NC, measure at 5v and read as 1s)
if (++bitsShiftedSoFar == 8)
SetInterrupt(IR_SR);
}
break;
case 2: // 010 = shift in by phi2
// SHIFT REGISTER BUG not implemented
// In both the shift in and shift out modes a liming condition may occur when the 6522 does not detect the shift pulse.
// This no shift condition occurs when CB1 and phi2 are asynchronous and their edges coincide.
if (!(bitsShiftedSoFar & 8)) // Shift register bug not implmented (would shift 9 bits?)
{
// should output cb1OutputShiftClock onto cb1
cb1OutputShiftClock = !cb1OutputShiftClock;
shiftRegister <<= 1;
shiftRegister |= cb2; // Should get from current cb2 (in a 1541 these pins on the VIAs are NC, measure at 5v and read as 1s)
if (++bitsShiftedSoFar == 8)
SetInterrupt(IR_SR);
}
break;
case 3: // 011 = shift in by external clock
// SHIFT REGISTER BUG not implemented
// In both the shift in and shift out modes a liming condition may occur when the 6522 does not detect the shift pulse.
// This no shift condition occurs when CB1 and phi2 are asynchronous and their edges coincide.
if (cb1Old && !cb1) // Negitive edge
{
if (!(bitsShiftedSoFar & 8)) // Shift register bug not implmented (would shift 9 bits?)
{
shiftRegister <<= 1;
shiftRegister |= cb2; // Should get from current cb2 (in a 1541 these pins on the VIAs are NC, measure at 5v and read as 1s)
if (++bitsShiftedSoFar == 8)
SetInterrupt(IR_SR);
}
}
break;
case 4: // 100 = free run shift out by timer 2 (keep shifting the same byte out over and over)
if (shiftClockPositiveEdge) // in this mode the shift register counter is disabled.
{
cb2Shift = (shiftRegister & 0x80) != 0;
shiftRegister = (shiftRegister << 1) | cb2Shift;
// should output cb1OutputShiftClock onto cb1
// cb2Shift should output to cb2
// - R/!W (on the 2nd VIA could be dangerous)
}
break;
case 5: // 101 = shift out by timer 2
if ((t2TimedOutCount > 2) && shiftClockPositiveEdge && !(bitsShiftedSoFar & 8))
{
cb2Shift = (shiftRegister & 0x80) != 0;
shiftRegister = (shiftRegister << 1) | cb2Shift;
if (++bitsShiftedSoFar == 8)
SetInterrupt(IR_SR);
// should output cb1OutputShiftClock onto cb1
// cb2Shift should output to cb2
// - R/!W (on the 2nd VIA could be dangerous)
}
break;
case 6: // 110 = shift out by phi2
if (!(bitsShiftedSoFar & 8))
{
// should output cb1OutputShiftClock onto cb1
cb1OutputShiftClock = !cb1OutputShiftClock;
cb2Shift = (shiftRegister & 0x80) != 0;
shiftRegister = (shiftRegister << 1) | cb2Shift;
if (++bitsShiftedSoFar == 8)
SetInterrupt(IR_SR);
// cb2Shift should output to cb2
// - R/!W (on the 2nd VIA could be dangerous)
}
break;
case 7: // 111 = shift out by external clock
// SHIFT REGISTER BUG not implemented
// In both the shift in and shift out modes a liming condition may occur when the 6522 does not detect the shift pulse.
// This no shift condition occurs when CB1 and phi2 are asynchronous and their edges coincide.
if (cb1Old && !cb1) // Negitive edge
{
if (!(bitsShiftedSoFar & 8))
{
// should output cb1OutputShiftClock onto cb1
cb1OutputShiftClock = !cb1OutputShiftClock;
cb2Shift = (shiftRegister & 0x80) != 0;
shiftRegister = (shiftRegister << 1) | cb2Shift;
if (++bitsShiftedSoFar == 8)
SetInterrupt(IR_SR);
// cb2Shift should output to cb2
// - R/!W (on the 2nd VIA could be dangerous)
}
}
break;
}
cb1Old = cb1;
}
unsigned char m6522::Read(unsigned int address)
{
unsigned char value = 0;
switch (address & 0xf)
{
case ORB:
value = ReadPortB();
if (t1OutPB7) // We need to see what we are setting eventhough we may not be outputting it (because off DDR)
{
if (!t1_pb7) value &= (~0x80);
else value |= 0x80;
}
break;
case ORA:
value = ReadPortA(true);
break;
case DDRB:
value = portB.GetDirection();
break;
case DDRA:
value = portA.GetDirection();
break;
case T1CL:
// A read T1CL transters the counters contents to the data bus and if a T1 interrupt has occurred the read operation will clear the IFR flag and reset !IRQ
ClearInterrupt(IR_T1);
value = t1c.bytes.l;
break;
case T1CH:
// A read T1CH transfers the counter's contents to the data bus.
value = t1c.bytes.h;
break;
case T1LL:
// A read of T1LL transfers the latchs contents to the data bus; it has no effect on the T1 interrupt flag.
value = t1l.bytes.l;
break;
case T1LH:
// A read of T1LH transfers the contents of the latch to the data bus.
value = t1l.bytes.h;
break;
case T2CL:
// A read of T2CL transfers the contents of the low order counter to the data bus, and if a T2 interrupt has occurred,
// the read operation will clear the T2 interrupt flag and reset !IRQ.
ClearInterrupt(IR_T2);
value = t2c.bytes.l;
break;
case T2CH:
// A read of T2CH transfers the contents of the high order counter to the data bus.
value = t2c.bytes.h;
break;
case SR:
value = shiftRegister;
if (interruptFlagRegister & IR_SR) bitsShiftedSoFar = 0;
ClearInterrupt(IR_SR);
break;
case ACR:
value = auxiliaryControlRegister;
break;
case FCR:
value = functionControlRegister;
break;
case IFR:
value = interruptFlagRegister;
break;
case IER:
value = interruptEnabledRegister | IR_IRQ;
break;
case ORA_NH:
value = ReadPortA(false);
break;
}
return value;
}
unsigned char m6522::Peek(unsigned int address)
{
unsigned char value = 0;
switch (address & 0xf)
{
case ORB:
value = PeekPortB();
break;
case ORA:
value = PeekPortA();
break;
case DDRB:
value = portB.GetDirection();
break;
case DDRA:
value = portA.GetDirection();
break;
case T1CL:
value = t1c.bytes.l;
break;
case T1CH:
value = t1c.bytes.h;
break;
case T1LL:
value = t1l.bytes.l;
break;
case T1LH:
value = t1l.bytes.h;
break;
case T2CL:
value = t2c.bytes.l;
break;
case T2CH:
value = t2c.bytes.h;
break;
case SR:
value = shiftRegister;
break;
case ACR:
value = auxiliaryControlRegister;
break;
case FCR:
value = functionControlRegister;
break;
case IFR:
value = interruptFlagRegister;
break;
case IER:
value = interruptEnabledRegister | IR_IRQ;
break;
case ORA_NH:
value = PeekPortA();
break;
}
return value;
}
void m6522::Write(unsigned int address, unsigned char value)
{
unsigned char ddr;
switch (address & 0xf)
{
case ORB:
WritePortB(value);
break;
case ORA:
WritePortA(value, true);
break;
case DDRB:
portB.SetDirection(value);
break;
case DDRA:
portA.SetDirection(value);
break;
case T1CL:
case T1LL:
// Writing to the T1CL is effectively a write to the low order latch.
// Writing T1LL stores an 8 bit count value into the latch. Effectively the same as a write to T1CL.
// The data is held in the latch until the high order counter is written : at this time the data is transferred to the counter.
t1l.bytes.l = value;
break;
case T1CH:
// A write to TICH loads both the high order counter and high order latch with the same value.
// Simultaneously the T1LL contents are transferred to the low order counter and the count begins.
// If PB7 has been programmed as a TIMER 1 output it will go low on the phi2 following the write operation.
// Additionally, if the T1 interrupt flag has already been set, the write operation will clear it.
// The write to TICH initiates the countdown on the next ph2.
t1l.bytes.h = value;
t1c.value = t1l.value;
t1Ticking = true; // BruceLee needs this else it will not load.
t1Reload = true;
ClearInterrupt(IR_T1);
t1FreeRunIRQsOn = true;
t1TimedOut = t1c.value == 0;
// By setting bit 7 in the ACH to a one, PB7 will be enabled as a one shot output. PB7 will go low immediately alter writing T1CH.
t1_pb7 = true;
if (t1OutPB7)
{
// With the output enabled(ACR7 = 1) a "write T1CH" operation will cause PB7 to go low. PB7 will return high when Timer 1 times out. The result is a single programmable width pulse.
// To guarantee a valid output level on PB7. Bit 7 of DDRB must also be set to a one. ORB bit 7 will NOT affect the level on PB7.
// TO CHECK - need to cache the old value of ORB bit 7?
ddr = portB.GetDirection();
if (ddr & 0x80)
portB.SetOutput(portB.GetOutput() & (~0x80));
}
if (!t1FreeRun) // If one shot mode then IRQ is back in play
t1OneShotTriggeredIRQ = false;
break;
case T1LH:
// A write to T1LH loads an 8 bit count value into the latch.
t1l.bytes.h = value;
// To clear or not to clear the IRQ flag?
// There are a few documents that say a write to T1LH does not clear the IRQ.
// Even Synertek's official FAQ doc (a document that was supposed to clear up the vagueness of the official datasheet) says that the flag is not cleared.
// I have now discovered that it is indeed cleared and this clear is very important.
// My take on it;-
// Allowing the IRQ to be cleared here allows a programmer to keep T1 running in free run mode but NOT generate IRQs (this is an undocumented feature).
// It can be useful to have T1 run in free run mode and utilise the benefits (ie timed pulses on PB7) but not incur the overhead of IRQs triggering.
// The designers of the 6522 allow this mode by clearing the T1 interrupt triggering when T1LH is written to.
if (!(interruptEnabledRegister & IR_T1)) // It appears that this only occurs if T1 IRQs are already disabled (else EOD refuses to load)
t1FreeRunIRQsOn = false;
ClearInterrupt(IR_T1);
break;
case T2LL:
// Writing T2CL/T2LL effectively stores an 8 bit byte in a write only latch where it will be held until the count is initiated.
t2Latch = value;
break;
case T2CH:
// Writing T2CH loads an 8 bit byte into the high order counter and latch (!!!there is no t2lh!!!) and simultaneously loads the low order latch into the low order counter, and the count down is initiated.
// If a T2 interrupt has occurred, the write operation will clear the T2 interrupt flag and reset !IRQ.
t2c.bytes.h = value;
t2c.bytes.l = t2Latch;
t2Reload = true;
t2TimedOutCount = 0;
t2LowTimedOut = false;
t2TimedOut = false;
t2CountingDown = true;
ClearInterrupt(IR_T2);
t2OneShotTriggeredIRQ = false;
break;
case SR:
shiftRegister = value;
if (interruptFlagRegister & IR_SR) bitsShiftedSoFar = 0;
ClearInterrupt(IR_SR);
cb1OutputShiftClock = 1;
cb1OutputShiftClockPositiveEdge = false;
break;
case ACR:
//bool t1OutPB7Prev = (auxiliaryControlRegister & ACR_T1_OUT_PB7) != 0;
auxiliaryControlRegister = value;
latchPortA = (value & ACR_PA_LATCH_ENABLE) != 0;
latchPortB = (value & ACR_PB_LATCH_ENABLE) != 0;
// T1 will generate continuous interrupts when bit 6 of the ACR is a one.
// In effect, this bit provides a link between the latches and counter; automatically loading the counters from the latches when time out occurs.
// Note: when in this mode and !IRQ is enabled. !IRQ will go low after the first (and each succeeding) time out and stay low until either TICL is read or T1CH is written.
t1FreeRun = (value & ACR_T1_MODE) != 0;
t1OutPB7 = (value & ACR_T1_OUT_PB7) != 0;
t2CountingPB6Mode = (value & ACR_T2_MODE) != 0;
// A precaution to take in the use of PB7 as the timer output concerns the Data Direction Register contents for PB7.
// Both DDRB bit 7 and ACR bit 7 must be 1 for PB7 to function as the timer output.
// If one is 1 and the other is 0, then PB7 functions as a normal output pin, controlled by ORB bit 7.
// TODO when in this mode cache and track what is occuring in ORB7?
if (t1OutPB7)
{
ddr = portB.GetDirection();
//if (ddr & 0x80)
//{
// // TO CHECK IN HW
// // If t1OutPB7 gets turned on before a time out what happens?
// // PB7 could become the cached value of the previously tracked PB7
// // PB7 could go low
// // PB7 could remain the value of ORB7 until the next time out
// if (!t1_pb7)
// portB.SetOutput(portB.GetOutput() & (~0x80));
// else
// portB.SetOutput(portB.GetOutput() | 0x80);
//}
if (!t1_pb7)
{
if (ddr & 0x80) portB.SetOutput(portB.GetOutput() & (~0x80));
}
else
{
if (ddr & 0x80) portB.SetOutput(portB.GetOutput() | 0x80);
}
}
//else if (t1OutPB7Prev)
//{
// // TODO: what if it was turned off before the timer times out?
// // What happens to PB7 in this case? It was low in one shot mode and can vary in free running mode, now what?
// // Should go back to the cached version of ORB7?
//}
break;
case FCR: // Peripheral Control Register
functionControlRegister = value;
if ((value & FCR_CA2_IO) == FCR_CA2_IO)
{
// ca2 is an output
pulseCA2 = (value & (FCR_CA2_OUTPUT_MODE1 | FCR_CA2_OUTPUT_MODE0)) == FCR_CA2_OUTPUT_MODE0;
ca2 = !pulseCA2 && (value & (FCR_CA2_OUTPUT_MODE1 | FCR_CA2_OUTPUT_MODE0)) == (FCR_CA2_OUTPUT_MODE1 | FCR_CA2_OUTPUT_MODE0);
}
else
{
// ca2 is an input
}
if ((value & FCR_CB2_IO) == FCR_CB2_IO)
{
// cb2 is an output
pulseCB2 = (value & (FCR_CB2_OUTPUT_MODE1 | FCR_CB2_OUTPUT_MODE0)) == FCR_CB2_OUTPUT_MODE0;
cb2 = !pulseCB2 && (value & (FCR_CB2_OUTPUT_MODE1 | FCR_CB2_OUTPUT_MODE0)) == (FCR_CB2_OUTPUT_MODE1 | FCR_CB2_OUTPUT_MODE0);
}
else
{
// cb2 is an input
}
break;
case IFR:
ClearInterrupt(value);
break;
case IER:
// If bit 7 is a 0, each 1 in bits 6 through 0 clears the corresponding bit in the IER.
// For each zero in bits 6 through 0, the corresponding bit is unaffected.
if (value & IR_IRQ) interruptEnabledRegister |= value;
else interruptEnabledRegister &= (~value);
interruptEnabledRegister &= (~IR_IRQ);
OutputIRQ();
break;
case ORA_NH:
WritePortA(value, false);
break;
}
}

385
m6522.h Normal file
View File

@ -0,0 +1,385 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#ifndef M6522_H
#define M6522_H
#include "IOPort.h"
#include "m6502.h"
class m6522
{
// $1800
// PB 0 data in
// PB 1 data out
// PB 2 clock in
// PB 3 clock out
// PB 4 ATNA
// PB 5,6 device address
// PB 7,CA1 ATN IN
// $1C00
// PB 0,1 step motor
// PB 2 MTR dirve motor
// PB 3 ACT drive LED
// PB 4 WPS write protect switch
// PB 5,6 bit rate
// PB 7 Sync
// CA 1 Byte ready
// CA 2 SOE set overflow enable 6502
// CB 2 read/write
// IFR
//REG 13 -- INTERRUPT FLAG REGISTER
//+-+-+-+-+-+-+-+-+
//|7|6|5|4|3|2|1|0| SET BY CLEARED BY
//+-+-+-+-+-+-+-+-+ +-----------------------+------------------------------+
// | | | | | | | +--CA2| CA2 ACTIVE EDGE | READ OR WRITE REG 1 (ORA)* |
// | | | | | | | +-----------------------+------------------------------+
// | | | | | | +--CA1--| CA1 ACTIVE EDGE | READ OR WRITE REG 1 (ORA) |
// | | | | | | +-----------------------+------------------------------+
// | | | | | +SHIFT REG| COMPLETE 8 SHIFTS | READ OR WRITE SHIFT REG |
// | | | | | +-----------------------+------------------------------+
// | | | | +-CB2-------| CB2 ACTIVE EDGE | READ OR WRITE ORB* |
// | | | | +-----------------------+------------------------------+
// | | | +-CB1---------| CB1 ACTIVE EDGE | READ OR WRITE ORB |
// | | | +-----------------------+------------------------------+
// | | +-TIMER 2-------| TIME-OUT OF T2 | READ T2 LOW OR WRITE T2 HIGH |
// | | +-----------------------+------------------------------+
// | +-TIMER 1---------| TIME-OUT OF T1 | READ T1 LOW OR WRITE T1 HIGH |
// | +-----------------------+------------------------------+
// +-IRQ---------------| ANY ENABLED INTERRUPT | CLEAR ALL INTERRUPTS |
// +-----------------------+------------------------------+
enum Registers
{
ORB, // 0 Port B
ORA, // 1 Port A
DDRB, // 2 Data direction register for port B
DDRA, // 3 Data direction register for port A
T1CL, // 4 Timer 1 count low
T1CH, // 5 Timer 1 count high
T1LL, // 6 Timer 1 latch low
T1LH, // 7 Timer 1 latch high
T2CL, // 8 Timer 2 count low read-only
T2LL = T2CL, // 8 Timer 2 latch low write-only
T2CH, // 9 Timer 2 count high read/write
SR, // 10 Serial port shift register
ACR, // 11 Auxiliary control register
FCR, // 12 Peripheral control register
IFR, // 13 Interrupt flag register
IER, // 14 Interrupt Enable Register
ORA_NH // 15 Port A with no handshake
};
enum ACR
{
ACR_PA_LATCH_ENABLE = 0x01, // Port A latch
// 0 = disabled
// 1 = enabled on CA1 transition (in)
ACR_PB_LATCH_ENABLE = 0x02, // Port B latch
// 0 = disabled
// 1 = enabled on CB1 transition (in/out)
ACR_SHIFTREG_CTRL = 0x1c, // Shift register control
// 000 = shift reg disabled
// 001 = shift in by timer 2
// 010 = shift in by phi2
// 011 = shift in by external clock (PB6?)
// 100 = free run shift out by timer 2 (keep shifting the same byte out over and over)
// 101 = shift out by timer 2
// 110 = shift out by phi2
// 111 = shift out by external clock(PB6 ? )
ACR_T2_MODE = 0x20, // Timer 2 control
// 0 = one shot (timed interrrupt)
// 1 = count down with pulses on PB6
ACR_T1_MODE = 0x40, // Timer 1 control
// 0 = one shot
// 1 = continuous, i.e. on underflow timer restarts at latch value.
ACR_T1_OUT_PB7 = 0x80 // Output on PB7
};
enum IR
{
IR_CA2 = 0x01, // CA2 flag
// Cleared by a read or write of ORA
IR_CA1 = 0x02, // CA1 flag
// Cleared by a read or write of ORA
IR_SR = 0x04, // Shift Register completion
// 1 at end of 8 shifts
// Cleared by read or write of SR
IR_CB2 = 0x08, // CB2 flag
// Cleared by a read or write of ORB
IR_CB1 = 0x10, // CB1 flag
// Cleared by a read or write of ORB
IR_T2 = 0x20, // Timer 2
// 1 when time out
// 0 after reading T2 low-byte counter or writing T2 high-byte counter
IR_T1 = 0x40, // Timer 1
// 1 when time out
// 0 after reading T1 low-byte counter or writing T1 high-byte latch
IR_IRQ = 0x80 // General interrupt status bit
// 1 if any interrupt active and enabled
// 0 when interrupt condition cleared
};
public:
/*
FCR/PCR
+---+---+---+---+---+---+---+---+
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+---+---+---+---+---+---+---+---+
| | | | | |
+----+----+ | +----+----+ |
| | | |
CB2 CONTROL -----+ | | +- CA1 INTERRUPT CONTROL
+-+-+-+------------------------+ | | +--------------------------+
|7|6|5| OPERATION | | | | 0 = NEGATIVE ACTIVE EDGE |
+-+-+-+------------------------+ | | | 1 = POSITIVE ACTIVE EDGE |
|0|0|0| INPUT NEG. ACTIVE EDGE | | | +--------------------------+
+-+-+-+------------------------+ | +---- CA2 INTERRUPT CONTROL
|0|0|1| INDEPENDENT INTERRUPT | | +-+-+-+------------------------+
| | | | INPUT NEGATIVE EDGE | | |3|2|1| OPERATION |
+-+-+-+------------------------+ | +-+-+-+------------------------+
|0|1|0| INPUT POS. ACTIVE EDGE | | |0|0|0| INPUT NEG. ACTIVE EDGE |
+-+-+-+------------------------+ | +-+-+-+------------------------+
|0|1|1| INDEPENDENT INTERRUPT | | |0|0|1| INDEPENDENT INTERRUPT |
| | | | INPUT POSITIVE EDGE | | | | | | INPUT NEGATIVE EDGE |
+-+-+-+------------------------+ | +-+-+-+------------------------+
|1|0|0| HANDSHAKE OUTPUT | | |0|1|0| INPUT POS. ACTIVE EDGE |
+-+-+-+------------------------+ | +-+-+-+------------------------+
|1|0|1| PULSE OUTPUT | | |0|1|1| INDEPENDENT INTERRUPT |
+-+-+-+------------------------+ | | | | | INPUT POSITIVE EDGE |
|1|1|0| LOW OUTPUT | | +-+-+-+------------------------+
+-+-+-+------------------------+ | |1|0|0| HANDSHAKE OUTPUT |
|1|1|1| HIGH OUTPUT | | +-+-+-+------------------------+
+-+-+-+------------------------+ | |1|0|1| PULSE OUTPUT |
CB1 INTERRUPT CONTROL --------+ +-+-+-+------------------------+
+--------------------------+ |1|1|0| LOW OUTPUT |
| 0 = NEGATIVE ACTIVE EDGE | +-+-+-+------------------------+
| 1 = POSITIVE ACTIVE EDGE | |1|1|1| HIGH OUTPUT |
+--------------------------+ +-+-+-+------------------------+
*/
enum FCR
{
FCR_CA1 = 0x01,
FCR_CA2_OUTPUT_MODE0 = 0x02, // 1c00 byte ready active 1541 rom $FAC1
FCR_CA2_OUTPUT_MODE1 = 0x04,
FCR_CA2_EDGE_TRIGGER_MODE = 0x04,
FCR_CA2_IO = 0x08,
FCR_CA2 = 0x0e,
FCR_CB1 = 0x01,
FCR_CB2_OUTPUT_MODE0 = 0x20, // 1c00 writing
FCR_CB2_OUTPUT_MODE1 = 0x40,
FCR_CB2_EDGE_TRIGGER_MODE = 0x40,
FCR_CB2_IO = 0x80,
FCR_CB2 = 0xe0,
};
m6522();
void Reset();
void ConnectIRQ(Interrupt* irq) { this->irq = irq; }
inline IOPort* GetPortA() { return &portA; }
inline bool GetLatchPortA() const { return latchPortA; }
inline unsigned char GetLatchedValueA() { return latchedValueA; }
inline bool GetCA1() { return ca1; }
void InputCA1(bool value);
inline bool GetCA2() { return ca2; }
void InputCA2(bool value);
inline IOPort* GetPortB() { return &portB; }
bool GetLatchPortB() const { return latchPortB; }
unsigned char GetLatchedValueB() { return latchedValueB; }
inline bool GetCB1() { return cb1; }
void InputCB1(bool value);
inline bool GetCB2() { return cb2; }
void InputCB2(bool value);
void Execute();
unsigned char Read(unsigned int address);
unsigned char Peek(unsigned int address);
void Write(unsigned int address, unsigned char value);
inline unsigned char GetFCR()
{
return functionControlRegister;
}
private:
inline unsigned char ReadPortB()
{
unsigned char ddr = portB.GetDirection();
unsigned char value = (latchPortB && (interruptFlagRegister & (unsigned char)IR_CB1) != 0) ? latchedValueB : (unsigned char)((portB.GetInput() & ~ddr) | (portB.GetOutput() & ddr));
ClearInterrupt(IR_CB1 | IR_CB2);
return value;
}
inline void WritePortB(unsigned char value)
{
ClearInterrupt(IR_CB1 | IR_CB2);
if ((functionControlRegister & (unsigned char)(FCR_CB2_IO | FCR_CB2_OUTPUT_MODE1)) == (unsigned char)(FCR_CB2_IO | FCR_CB2_OUTPUT_MODE1))
cb2 = false;
portB.SetOutput(value);
}
inline unsigned char ReadPortA(bool handshake)
{
unsigned char ddr = portA.GetDirection();
unsigned char value = (latchPortA && (interruptFlagRegister & (unsigned char)IR_CA1) != 0) ? latchedValueA : (unsigned char)((portA.GetInput() & ~ddr) | (portA.GetOutput() & ddr));
if (handshake)
ClearInterrupt(IR_CA1 | IR_CA2);
return value;
}
inline unsigned char PeekPortA()
{
unsigned char ddr = portA.GetDirection();
unsigned char value = (latchPortA && (interruptFlagRegister & (unsigned char)IR_CA1) != 0) ? latchedValueA : (unsigned char)((portA.GetInput() & ~ddr) | (portA.GetOutput() & ddr));
return value;
}
inline void WritePortA(unsigned char value, bool handshake)
{
if (handshake)
{
ClearInterrupt(IR_CA1 | IR_CA2);
if ((functionControlRegister & (unsigned char)(FCR_CA2_IO | FCR_CA2_OUTPUT_MODE1)) == (unsigned char)(FCR_CA2_IO | FCR_CA2_OUTPUT_MODE1))
ca2 = false;
}
portA.SetOutput(value);
}
inline unsigned char PeekPortB()
{
unsigned char ddr = portB.GetDirection();
unsigned char value = (latchPortB && (interruptFlagRegister & (unsigned char)IR_CB1) != 0) ? latchedValueB : (unsigned char)((portB.GetInput() & ~ddr) | (portB.GetOutput() & ddr));
return value;
}
inline void SetInterrupt(unsigned char flag)
{
if (!(interruptFlagRegister & flag))
{
interruptFlagRegister |= flag;
OutputIRQ();
}
}
inline void ClearInterrupt(unsigned char flag)
{
if (interruptFlagRegister & flag)
{
interruptFlagRegister &= ~flag;
OutputIRQ();
}
}
inline void OutputIRQ()
{
if (interruptEnabledRegister & interruptFlagRegister & 0x7f)
{
if ((interruptFlagRegister & IR_IRQ) == 0)
{
interruptFlagRegister |= IR_IRQ;
if (irq) irq->Assert();
}
}
else
{
if (interruptFlagRegister & IR_IRQ)
{
interruptFlagRegister &= ~IR_IRQ;
if (irq) irq->Release();
}
}
}
struct Counter
{
union
{
unsigned short value;
struct
{
// if porting to big endian, swap these.
unsigned char l;
unsigned char h;
} bytes;
};
};
Interrupt* irq;
unsigned char functionControlRegister;
unsigned char auxiliaryControlRegister;
IOPort portA;
bool latchPortA;
unsigned char latchedValueA;
bool ca1;
bool ca2;
bool pulseCA2;
IOPort portB;
bool latchPortB;
unsigned char latchedValueB;
bool cb1;
bool cb1Old;
bool cb2;
bool pulseCB2;
Counter t1c;
Counter t1l;
bool t1Ticking;
bool t1Reload;
bool t1OutPB7;
bool t1FreeRun;
bool t1FreeRunIRQsOn;
bool t1TimedOut;
bool t1_pb7;
bool t1OneShotTriggeredIRQ;
Counter t2c;
unsigned char t2Latch;
bool t2Reload;
bool t2CountingDown;
bool t2CountingPB6ModeOld;
bool t2CountingPB6Mode;
bool t2TimedOut;
bool t2LowTimedOut;
bool t2OneShotTriggeredIRQ;
unsigned t2TimedOutCount;
unsigned char pb6Old;
unsigned char interruptFlagRegister;
unsigned char interruptEnabledRegister;
unsigned char shiftRegister;
unsigned bitsShiftedSoFar;
unsigned cb1OutputShiftClock;
unsigned char cb2Shift; // version of cb2 controlled by the shift register
bool cb1OutputShiftClockPositiveEdge;
};
#endif

1123
main.cpp Normal file

File diff suppressed because it is too large Load Diff

300
options.cpp Normal file
View File

@ -0,0 +1,300 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#include "options.h"
#include <string.h>
#include <strings.h>
#include <ctype.h>
#define INVALID_VALUE ((unsigned) -1)
char* TextParser::GetToken(bool includeSpace)
{
bool isSpace;
do
{
} while (ParseComment());
while (*data != '\0')
{
isSpace = isspace(*data);
if (!isSpace || (includeSpace && isSpace))
break;
data++;
}
if (*data == '\0')
return 0;
char* pToken = data;
while (*data != '\0')
{
isSpace = isspace(*data);
if ((!includeSpace && isSpace) || (*data == '\n') || (*data == '\r'))
{
*data++ = '\0';
break;
}
data++;
}
return pToken;
}
void TextParser::SkipWhiteSpace()
{
while (*data != '\0')
{
if (!isspace(*data))
break;
data++;
}
}
bool TextParser::ParseComment()
{
SkipWhiteSpace();
if (*data == '\0')
return 0;
if (data[0] != '/')
return false;
if (data[1] == '/')
{
// One line comment
data += 2;
while (*data)
{
if (*data == '\n')
break;
data++;
}
SkipWhiteSpace();
return true;
}
else if (data[1] == '*')
{
// Multiline comment
data += 2;
while (*data)
{
if (*data++ == '*' && *data && *data == '/')
{
data++;
break;
}
}
SkipWhiteSpace();
return true;
}
return false;
}
Options::Options(void)
: TextParser()
, deviceID(8)
, onResetChangeToStartingFolder(0)
, extraRAM(0)
, disableSD2IECCommands(0)
, supportUARTInput(0)
, graphIEC(0)
, quickBoot(0)
, displayPNGIcons(0)
, soundOnGPIO(0)
, invertIECInputs(0)
, splitIECLines(0)
{
ROMName[0] = 0;
ROMNameSlot2[0] = 0;
ROMNameSlot3[0] = 0;
ROMNameSlot4[0] = 0;
ROMNameSlot5[0] = 0;
ROMNameSlot6[0] = 0;
ROMNameSlot7[0] = 0;
ROMNameSlot8[0] = 0;
}
void Options::Process(char* buffer)
{
SetData(buffer);
char* pOption;
while ((pOption = GetToken()) != 0)
{
/*char* equals = */GetToken();
char* pValue = GetToken();
if (strcasecmp(pOption, "deviceID") == 0)
{
unsigned nValue = 0;
if ((nValue = GetDecimal(pValue)) != INVALID_VALUE)
deviceID = nValue;
}
else if (strcasecmp(pOption, "OnResetChangeToStartingFolder") == 0)
{
unsigned nValue = 0;
if ((nValue = GetDecimal(pValue)) != INVALID_VALUE)
onResetChangeToStartingFolder = nValue;
}
else if (strcasecmp(pOption, "ExtraRAM") == 0)
{
unsigned nValue = 0;
if ((nValue = GetDecimal(pValue)) != INVALID_VALUE)
extraRAM = nValue;
}
else if (strcasecmp(pOption, "DisableSD2IECCommands") == 0)
{
unsigned nValue = 0;
if ((nValue = GetDecimal(pValue)) != INVALID_VALUE)
disableSD2IECCommands = nValue;
}
else if (strcasecmp(pOption, "SupportUARTInput") == 0)
{
unsigned nValue = 0;
if ((nValue = GetDecimal(pValue)) != INVALID_VALUE)
supportUARTInput = nValue;
}
else if (strcasecmp(pOption, "GraphIEC") == 0)
{
unsigned nValue = 0;
if ((nValue = GetDecimal(pValue)) != INVALID_VALUE)
graphIEC = nValue;
}
else if (strcasecmp(pOption, "QuickBoot") == 0)
{
unsigned nValue = 0;
if ((nValue = GetDecimal(pValue)) != INVALID_VALUE)
quickBoot = nValue;
}
else if (strcasecmp(pOption, "DisplayPNGIcons") == 0)
{
unsigned nValue = 0;
if ((nValue = GetDecimal(pValue)) != INVALID_VALUE)
displayPNGIcons = nValue;
}
else if (strcasecmp(pOption, "soundOnGPIO") == 0)
{
unsigned nValue = 0;
if ((nValue = GetDecimal(pValue)) != INVALID_VALUE)
soundOnGPIO = nValue;
}
else if (strcasecmp(pOption, "invertIECInputs") == 0)
{
unsigned nValue = 0;
if ((nValue = GetDecimal(pValue)) != INVALID_VALUE)
invertIECInputs = nValue;
}
else if (strcasecmp(pOption, "splitIECLines") == 0)
{
unsigned nValue = 0;
if ((nValue = GetDecimal(pValue)) != INVALID_VALUE)
splitIECLines = nValue;
}
else if ((strcasecmp(pOption, "ROM") == 0) || (strcasecmp(pOption, "ROM1") == 0))
{
strncpy(ROMName, pValue, 255);
}
else if (strcasecmp(pOption, "ROM2") == 0)
{
strncpy(ROMNameSlot2, pValue, 255);
}
else if (strcasecmp(pOption, "ROM3") == 0)
{
strncpy(ROMNameSlot3, pValue, 255);
}
else if (strcasecmp(pOption, "ROM4") == 0)
{
strncpy(ROMNameSlot4, pValue, 255);
}
else if (strcasecmp(pOption, "ROM5") == 0)
{
strncpy(ROMNameSlot5, pValue, 255);
}
else if (strcasecmp(pOption, "ROM6") == 0)
{
strncpy(ROMNameSlot6, pValue, 255);
}
else if (strcasecmp(pOption, "ROM7") == 0)
{
strncpy(ROMNameSlot7, pValue, 255);
}
else if (strcasecmp(pOption, "ROM8") == 0)
{
strncpy(ROMNameSlot8, pValue, 255);
}
}
}
unsigned Options::GetDecimal(char* pString)
{
if (pString == 0 || *pString == '\0')
return INVALID_VALUE;
unsigned nResult = 0;
char chChar = *pString++;
while (chChar != '\0' && chChar != 13)
{
if (!('0' <= chChar && chChar <= '9'))
return INVALID_VALUE;
unsigned nPrevResult = nResult;
nResult = nResult * 10 + (chChar - '0');
if (nResult < nPrevResult || nResult == INVALID_VALUE)
return INVALID_VALUE;
chChar = *pString++;
}
return nResult;
}
const char* Options::GetRomName(int index) const
{
switch (index)
{
case 1:
return ROMNameSlot2;
case 2:
return ROMNameSlot3;
case 3:
return ROMNameSlot4;
case 4:
return ROMNameSlot5;
case 5:
return ROMNameSlot6;
case 6:
return ROMNameSlot7;
case 7:
return ROMNameSlot8;
}
return ROMName;
}

85
options.h Normal file
View File

@ -0,0 +1,85 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#ifndef OPTIONS_H
#define OPTIONS_H
class TextParser
{
public:
TextParser(void)
: data(0)
{
}
void SetData(char* buffer) { data = buffer; }
char* GetToken(bool includeSpace = false);
protected:
char* data;
bool ParseComment();
void SkipWhiteSpace();
};
class Options : public TextParser
{
public:
Options(void);
void Process(char* buffer);
unsigned int GetDeviceID() const { return deviceID; }
unsigned int GetOnResetChangeToStartingFolder() const { return onResetChangeToStartingFolder; }
const char* GetRomName(int index) const;
unsigned int GetExtraRAM() const { return extraRAM; }
unsigned int GetDisableSD2IECCommands() const { return disableSD2IECCommands; }
unsigned int GetSupportUARTInput() const { return supportUARTInput; }
unsigned int GraphIEC() const { return graphIEC; }
unsigned int QuickBoot() const { return quickBoot; }
unsigned int DisplayPNGIcons() const { return displayPNGIcons; }
unsigned int SoundOnGPIO() const { return soundOnGPIO; }
unsigned int SplitIECLines() const { return splitIECLines; }
unsigned int InvertIECInputs() const { return invertIECInputs; }
static unsigned GetDecimal(char* pString);
private:
unsigned int deviceID;
unsigned int onResetChangeToStartingFolder;
unsigned int extraRAM;
unsigned int disableSD2IECCommands;
unsigned int supportUARTInput;
unsigned int graphIEC;
unsigned int quickBoot;
unsigned int displayPNGIcons;
unsigned int soundOnGPIO;
unsigned int invertIECInputs;
unsigned int splitIECLines;
char ROMName[256];
char ROMNameSlot2[256];
char ROMNameSlot3[256];
char ROMNameSlot4[256];
char ROMNameSlot5[256];
char ROMNameSlot6[256];
char ROMNameSlot7[256];
char ROMNameSlot8[256];
};
#endif

190
prot.cpp Normal file
View File

@ -0,0 +1,190 @@
/*
* Protection handlers for MNIB
* Copyright 2004-2005 Pete Rittwage <peter(at)rittwage(dot)com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gcr.h"
#include "prot.h"
void
shift_buffer(BYTE * buffer, int length, int n)
{
int i;
BYTE tempbuf[NIB_TRACK_LENGTH];
BYTE carry;
int carryshift;
carryshift = 8 - n;
memcpy(tempbuf, buffer, length);
// shift buffer right by n bits
carry = tempbuf[length - 1];
for (i = 0; i < length; i++)
{
buffer[i] = (tempbuf[i] >> n) | (carry << carryshift);
carry = tempbuf[i];
}
}
BYTE *
align_vmax(BYTE * work_buffer, int tracklen)
{
BYTE *pos, *buffer_end, *start_pos;
int run;
run = 0;
pos = work_buffer;
start_pos = work_buffer;
buffer_end = work_buffer + tracklen;
/* Try to find V-MAX track marker bytes
0x4b (cinemaware track 20)
0xa5 (cinemaware other tracks)
0x5a (misc)
0x49 (misc)
*/
while (pos < buffer_end)
{
// duplicator's markers
if ((*pos == 0x4b) || (*pos == 0x5a) || (*pos == 0x49) || (*pos == 0xa5))
{
if(!run) start_pos = pos; // mark first byte
if (run > 5) return (start_pos); // assume this is it
run++;
}
else
run = 0;
pos++;
}
return (0);
}
// Line up the track cycle to the start of the longest gap mark
// this helps some custom protection tracks master properly
BYTE *
auto_gap(BYTE * work_buffer, int tracklen)
{
BYTE *pos, *buffer_end, *key_temp, *key;
int run, longest;
run = 0;
longest = 0;
pos = work_buffer;
buffer_end = work_buffer + tracklen;
key = key_temp = NULL;
/* try to find longest good gcr run */
while (pos < buffer_end - 2)
{
if (*pos == *(pos + 1)) // && (*pos != 0x00 ))
{
key_temp = pos + 2;
run++;
}
else
{
if (run > longest)
{
key = key_temp;
longest = run;
//gapbyte = *pos;
}
run = 0;
}
pos++;
}
/* last 5 bytes of gap */
// printf("gapbyte: %x, len: %d\n",gapbyte,longest);
if(key >= work_buffer + 5)
return(key - 5);
else
return(key);
}
// The idea behind this is that weak bits commonly occur
// at the ends of tracks when they were mastered.
// we can line up the track cycle to this
// in lieu of no other hints
BYTE *
find_weak_gap(BYTE * work_buffer, int tracklen)
{
BYTE *pos, *buffer_end, *key_temp, *key;
int run, longest;
run = 0;
longest = 0;
pos = work_buffer;
buffer_end = work_buffer + tracklen;
key = key_temp = NULL;
/* try to find longest bad gcr run */
while (pos < buffer_end)
{
if (is_bad_gcr(work_buffer, buffer_end - work_buffer,
pos - work_buffer))
{
// mark next GCR byte
key_temp = pos + 1;
run++;
}
else
{
if (run > longest)
{
key = key_temp;
longest = run;
}
run = 0;
}
pos++;
}
/* first byte after bad run */
return (key);
}
// Line up the track cycle to the start of the longest sync mark
// this helps some custom protection tracks master properly
BYTE *
find_long_sync(BYTE * work_buffer, int tracklen)
{
BYTE *pos, *buffer_end, *key_temp, *key;
int run, longest;
run = 0;
longest = 0;
pos = work_buffer;
buffer_end = work_buffer + tracklen;
key = key_temp = NULL;
/* try to find longest sync run */
while (pos < buffer_end)
{
if (*pos == 0xff)
{
if (run == 0)
key_temp = pos;
run++;
}
else
{
if (run > longest)
{
key = key_temp;
longest = run;
}
run = 0;
}
pos++;
}
/* first byte of longest sync run */
return (key);
}

8
prot.h Normal file
View File

@ -0,0 +1,8 @@
/* prot.h */
void shift_buffer(BYTE * buffer, int length, int n);
BYTE *align_vmax(BYTE * work_buffer, int track_len);
BYTE *auto_gap(BYTE * work_buffer, int track_len);
BYTE *find_weak_gap(BYTE * work_buffer, int tracklen);
BYTE *find_long_sync(BYTE * work_buffer, int tracklen);
BYTE *auto_gap(BYTE * work_buffer, int tracklen);

213
rpi-aux.c Normal file
View File

@ -0,0 +1,213 @@
#include <stdio.h>
#include "rpi-aux.h"
#include "rpi-base.h"
#include "rpi-gpio.h"
#include "startup.h"
#include "stdlib.h"
#include "rpiHardware.h"
#ifdef INCLUDE_DEBUGGER
#include "debugger/debugger.h"
#endif
/* Define the system clock frequency in MHz for the baud rate calculation.
This is clearly defined on the BCM2835 datasheet errata page:
http://elinux.org/BCM2835_datasheet_errata */
#define FALLBACK_SYS_FREQ 250000000
//#define USE_IRQ
#define TX_BUFFER_SIZE 65536 // Must be a power of 2
static aux_t* auxillary = (aux_t*) AUX_BASE;
aux_t* RPI_GetAux(void)
{
return auxillary;
}
#if defined(USE_IRQ)
#include "rpi-interrupts.h"
// Note, at the point the MiniUART is initialized, low vectors are in use
#define IRQ_VECTOR 0x38
static char *tx_buffer;
static volatile int tx_head;
static volatile int tx_tail;
static void __attribute__((interrupt("IRQ"))) RPI_AuxMiniUartIRQHandler() {
_data_memory_barrier();
while (1) {
int iir = auxillary->MU_IIR;
if (iir & AUX_MUIIR_INT_NOT_PENDING) {
/* No more interrupts */
break;
}
/* Handle RxReady interrupt */
if (iir & AUX_MUIIR_INT_IS_RX) {
#ifdef INCLUDE_DEBUGGER
/* Forward all received characters to the debugger */
debugger_rx_char(auxillary->MU_IO & 0xFF);
#else
/* Else just exho characters */
RPI_AuxMiniUartWrite(auxillary->MU_IO & 0xFF);
#endif
}
/* Handle TxEmpty interrupt */
if (iir & AUX_MUIIR_INT_IS_TX) {
if (tx_tail != tx_head) {
/* Transmit the character */
tx_tail = (tx_tail + 1) & (TX_BUFFER_SIZE - 1);
auxillary->MU_IO = tx_buffer[tx_tail];
} else {
/* Disable TxEmpty interrupt */
auxillary->MU_IER &= ~AUX_MUIER_TX_INT;
}
}
}
_data_memory_barrier();
}
#endif
void RPI_AuxMiniUartInit(int baud, int bits)
{
volatile int i;
// Data memory barrier need to be places between accesses to different peripherals
//
// See page 7 of the BCM2853 manual
_data_memory_barrier();
int sys_freq = get_clock_rate(CORE_CLK_ID);
if (!sys_freq) {
sys_freq = FALLBACK_SYS_FREQ;
}
_data_memory_barrier();
/* Setup GPIO 14 and 15 as alternative function 5 which is
UART 1 TXD/RXD. These need to be set before enabling the UART */
RPI_SetGpioPinFunction(RPI_GPIO14, FS_ALT5);
RPI_SetGpioPinFunction(RPI_GPIO15, FS_ALT5);
_data_memory_barrier();
// Enable weak pullups
RPI_GpioBase->GPPUD = 2;
// Note: the delay values are important, with 150 the receiver did not work!
for (i = 0; i < 1000; i++)
{
}
RPI_GpioBase->GPPUDCLK0 = (1 << 14) | (1 << 15);
// Note: the delay values are important, with 150 the receiver did not work!
for (i = 0; i < 1000; i++)
{
}
RPI_GpioBase->GPPUD = 2;
RPI_GpioBase->GPPUDCLK0 = 0;
_data_memory_barrier();
/* As this is a mini uart the configuration is complete! Now just
enable the uart. Note from the documentation in section 2.1.1 of
the ARM peripherals manual:
If the enable bits are clear you will have no access to a
peripheral. You can not even read or write the registers */
auxillary->ENABLES = AUX_ENA_MINIUART;
_data_memory_barrier();
/* Disable flow control,enable transmitter and receiver! */
auxillary->MU_CNTL = 0;
/* Decide between seven or eight-bit mode */
if (bits == 8)
auxillary->MU_LCR = AUX_MULCR_8BIT_MODE;
else
auxillary->MU_LCR = 0;
auxillary->MU_MCR = 0;
/* Disable all interrupts from MU and clear the fifos */
auxillary->MU_IER = 0;
auxillary->MU_IIR = 0xC6;
/* Transposed calculation from Section 2.2.1 of the ARM peripherals manual */
auxillary->MU_BAUD = ( sys_freq / (8 * baud)) - 1;
#ifdef USE_IRQ
tx_buffer = malloc(TX_BUFFER_SIZE);
tx_head = tx_tail = 0;
*((uint32_t *) IRQ_VECTOR) = (uint32_t) RPI_AuxMiniUartIRQHandler;
_data_memory_barrier();
RPI_GetIrqController()->Enable_IRQs_1 = (1 << 29);
_data_memory_barrier();
auxillary->MU_IER |= AUX_MUIER_RX_INT;
#endif
_data_memory_barrier();
/* Disable flow control,enable transmitter and receiver! */
auxillary->MU_CNTL = AUX_MUCNTL_TX_ENABLE | AUX_MUCNTL_RX_ENABLE;
_data_memory_barrier();
}
void RPI_AuxMiniUartWrite(char c)
{
#ifdef USE_IRQ
int tmp_head = (tx_head + 1) & (TX_BUFFER_SIZE - 1);
/* Test if the buffer is full */
if (tmp_head == tx_tail) {
int cpsr = _get_cpsr();
if (cpsr & 0x80) {
/* IRQ disabled: drop the character to avoid deadlock */
return;
} else {
/* IRQ enabled: wait for space in buffer */
while (tmp_head == tx_tail) {
}
}
}
/* Buffer the character */
tx_buffer[tmp_head] = c;
tx_head = tmp_head;
_data_memory_barrier();
/* Enable TxEmpty interrupt */
auxillary->MU_IER |= AUX_MUIER_TX_INT;
_data_memory_barrier();
#else
/* Wait until the UART has an empty space in the FIFO */
while ((auxillary->MU_LSR & AUX_MULSR_TX_EMPTY) == 0)
{
}
/* Write the character to the FIFO for transmission */
auxillary->MU_IO = c;
#endif
}
extern void RPI_EnableUart(char* pMessage)
{
RPI_AuxMiniUartInit(115200, 8); // Initialise the UART
printf(pMessage);
}

200
rpi-aux.h Normal file
View File

@ -0,0 +1,200 @@
/*
Part of the Raspberry-Pi Bare Metal Tutorials
Copyright (c) 2013-2015, Brian Sidebotham
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef RPI_AUX_H
#define RPI_AUX_H
#include "rpi-base.h"
/* Although these values were originally from the BCM2835 Arm peripherals PDF
it's clear that was rushed and has some glaring errors - so these values
may appear to be different. These values have been changed due to data on
the elinux BCM2835 datasheet errata:
http://elinux.org/BCM2835_datasheet_errata */
#define AUX_BASE ( PERIPHERAL_BASE + 0x215000 )
#define AUX_ENA_MINIUART ( 1 << 0 )
#define AUX_ENA_SPI1 ( 1 << 1 )
#define AUX_ENA_SPI2 ( 1 << 2 )
#define AUX_IRQ_SPI2 ( 1 << 2 )
#define AUX_IRQ_SPI1 ( 1 << 1 )
#define AUX_IRQ_MU ( 1 << 0 )
#define AUX_MULCR_8BIT_MODE ( 3 << 0 ) /* See errata for this value */
#define AUX_MULCR_BREAK ( 1 << 6 )
#define AUX_MULCR_DLAB_ACCESS ( 1 << 7 )
#define AUX_MUMCR_RTS ( 1 << 1 )
#define AUX_MULSR_DATA_READY ( 1 << 0 )
#define AUX_MULSR_RX_OVERRUN ( 1 << 1 )
#define AUX_MULSR_TX_EMPTY ( 1 << 5 )
#define AUX_MULSR_TX_IDLE ( 1 << 6 )
#define AUX_MUMSR_CTS ( 1 << 5 )
#define AUX_MUCNTL_RX_ENABLE ( 1 << 0 )
#define AUX_MUCNTL_TX_ENABLE ( 1 << 1 )
#define AUX_MUCNTL_RTS_FLOW ( 1 << 2 )
#define AUX_MUCNTL_CTS_FLOW ( 1 << 3 )
#define AUX_MUCNTL_RTS_FIFO ( 3 << 4 )
#define AUX_MUCNTL_RTS_ASSERT ( 1 << 6 )
#define AUX_MUCNTL_CTS_ASSERT ( 1 << 7 )
#define AUX_MUSTAT_SYMBOL_AV ( 1 << 0 )
#define AUX_MUSTAT_SPACE_AV ( 1 << 1 )
#define AUX_MUSTAT_RX_IDLE ( 1 << 2 )
#define AUX_MUSTAT_TX_IDLE ( 1 << 3 )
#define AUX_MUSTAT_RX_OVERRUN ( 1 << 4 )
#define AUX_MUSTAT_TX_FIFO_FULL ( 1 << 5 )
#define AUX_MUSTAT_RTS ( 1 << 6 )
#define AUX_MUSTAT_CTS ( 1 << 7 )
#define AUX_MUSTAT_TX_EMPTY ( 1 << 8 )
#define AUX_MUSTAT_TX_DONE ( 1 << 9 )
#define AUX_MUSTAT_RX_FIFO_LEVEL ( 7 << 16 )
#define AUX_MUSTAT_TX_FIFO_LEVEL ( 7 << 24 )
// Interrupt enables are incorrect on page 12 of:
// https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
// See errata:
// http://elinux.org/BCM2835_datasheet_errata#p12
#define AUX_MUIER_TX_INT ( (1 << 1) )
#define AUX_MUIER_RX_INT ( (1 << 0 )| (1 << 2) )
#define AUX_MUIIR_INT_NOT_PENDING ( 1 << 0 )
#define AUX_MUIIR_INT_IS_TX ( 1 << 1 )
#define AUX_MUIIR_INT_IS_RX ( 1 << 2 )
#define FSEL0(x) ( x )
#define FSEL1(x) ( x << 3 )
#define FSEL2(x) ( x << 6 )
#define FSEL3(x) ( x << 9 )
#define FSEL4(x) ( x << 12 )
#define FSEL5(x) ( x << 15 )
#define FSEL6(x) ( x << 18 )
#define FSEL7(x) ( x << 21 )
#define FSEL8(x) ( x << 24 )
#define FSEL9(x) ( x << 27 )
#define FSEL10(x) ( x )
#define FSEL11(x) ( x << 3 )
#define FSEL12(x) ( x << 6 )
#define FSEL13(x) ( x << 9 )
#define FSEL14(x) ( x << 12 )
#define FSEL15(x) ( x << 15 )
#define FSEL16(x) ( x << 18 )
#define FSEL17(x) ( x << 21 )
#define FSEL18(x) ( x << 24 )
#define FSEL19(x) ( x << 27 )
#define FSEL20(x) ( x )
#define FSEL21(x) ( x << 3 )
#define FSEL22(x) ( x << 6 )
#define FSEL23(x) ( x << 9 )
#define FSEL24(x) ( x << 12 )
#define FSEL25(x) ( x << 15 )
#define FSEL26(x) ( x << 18 )
#define FSEL27(x) ( x << 21 )
#define FSEL28(x) ( x << 24 )
#define FSEL29(x) ( x << 27 )
#define FSEL30(x) ( x )
#define FSEL31(x) ( x << 3 )
#define FSEL32(x) ( x << 6 )
#define FSEL33(x) ( x << 9 )
#define FSEL34(x) ( x << 12 )
#define FSEL35(x) ( x << 15 )
#define FSEL36(x) ( x << 18 )
#define FSEL37(x) ( x << 21 )
#define FSEL38(x) ( x << 24 )
#define FSEL39(x) ( x << 27 )
#define FSEL40(x) ( x )
#define FSEL41(x) ( x << 3 )
#define FSEL42(x) ( x << 6 )
#define FSEL43(x) ( x << 9 )
#define FSEL44(x) ( x << 12 )
#define FSEL45(x) ( x << 15 )
#define FSEL46(x) ( x << 18 )
#define FSEL47(x) ( x << 21 )
#define FSEL48(x) ( x << 24 )
#define FSEL49(x) ( x << 27 )
#define FSEL50(x) ( x )
#define FSEL51(x) ( x << 3 )
#define FSEL52(x) ( x << 6 )
#define FSEL53(x) ( x << 9 )
typedef struct
{
volatile unsigned int IRQ;
volatile unsigned int ENABLES;
volatile unsigned int reserved1[((0x40 - 0x04) / 4) - 1];
volatile unsigned int MU_IO;
volatile unsigned int MU_IER;
volatile unsigned int MU_IIR;
volatile unsigned int MU_LCR;
volatile unsigned int MU_MCR;
volatile unsigned int MU_LSR;
volatile unsigned int MU_MSR;
volatile unsigned int MU_SCRATCH;
volatile unsigned int MU_CNTL;
volatile unsigned int MU_STAT;
volatile unsigned int MU_BAUD;
volatile unsigned int reserved2[(0x80 - 0x68) / 4];
volatile unsigned int SPI0_CNTL0;
volatile unsigned int SPI0_CNTL1;
volatile unsigned int SPI0_STAT;
volatile unsigned int SPI0_IO;
volatile unsigned int SPI0_PEEK;
volatile unsigned int reserved3[(0xC0 - 0x94) / 4];
volatile unsigned int SPI1_CNTL0;
volatile unsigned int SPI1_CNTL1;
volatile unsigned int SPI1_STAT;
volatile unsigned int SPI1_IO;
volatile unsigned int SPI1_PEEK;
} aux_t;
extern aux_t* RPI_GetAux(void);
extern void RPI_AuxMiniUartInit(int baud, int bits);
extern void RPI_AuxMiniUartWrite(char c);
extern void RPI_EnableUart(char* pMessage);
#endif

73
rpi-base.h Normal file
View File

@ -0,0 +1,73 @@
/*
Part of the Raspberry-Pi Bare Metal Tutorials
Copyright (c) 2013-2015, Brian Sidebotham
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef RPI_BASE_H
#define RPI_BASE_H
#ifdef __ASSEMBLER__
#if defined(RPI2) || defined(RPI3)
#define PERIPHERAL_BASE 0x3F000000
#else
#define PERIPHERAL_BASE 0x20000000
#endif
#else
#if defined(RPI2) || defined(RPI3)
#define PERIPHERAL_BASE 0x3F000000UL
#else
#define PERIPHERAL_BASE 0x20000000UL
#endif
//#define GPU_IO_BASE 0x7E000000
//#define GPU_CACHED_BASE 0x40000000
//#define GPU_UNCACHED_BASE 0xC0000000
//
//#if defined ( RPI2 ) || defined (RPI3)
//#define GPU_MEM_BASE GPU_UNCACHED_BASE
//#else
//#define GPU_MEM_BASE GPU_CACHED_BASE
//#endif
//#define MEM_COHERENT_REGION 0x400000
#include <stdint.h>
typedef volatile uint32_t rpi_reg_rw_t;
typedef volatile const uint32_t rpi_reg_ro_t;
typedef volatile uint32_t rpi_reg_wo_t;
typedef volatile uint64_t rpi_wreg_rw_t;
typedef volatile const uint64_t rpi_wreg_ro_t;
#endif
#endif

336
rpi-gpio.c Normal file
View File

@ -0,0 +1,336 @@
#include <stdint.h>
#include "rpi-gpio.h"
#include "rpiHardware.h"
//#include "rpi-mailbox-interface.h"
#include "rpi-mailbox.h"
rpi_gpio_t* RPI_GpioBase = (rpi_gpio_t*) RPI_GPIO_BASE;
void RPI_SetGpioPinFunction(rpi_gpio_pin_t gpio, rpi_gpio_alt_function_t func)
{
rpi_reg_rw_t* fsel_reg = &RPI_GpioBase->GPFSEL[gpio / 10];
rpi_reg_rw_t fsel_copy = *fsel_reg;
fsel_copy &= ~(FS_MASK << ((gpio % 10) * 3));
fsel_copy |= (func << ((gpio % 10) * 3));
*fsel_reg = fsel_copy;
}
void RPI_SetGpioOutput(rpi_gpio_pin_t gpio)
{
RPI_SetGpioPinFunction(gpio, FS_OUTPUT);
}
void RPI_SetGpioInput(rpi_gpio_pin_t gpio)
{
RPI_SetGpioPinFunction(gpio, FS_INPUT);
}
rpi_gpio_value_t RPI_GetGpioValue(rpi_gpio_pin_t gpio)
{
rpi_gpio_value_t result = RPI_IO_UNKNOWN;
switch (gpio / 32)
{
case 0:
result = RPI_GpioBase->GPLEV0[0] & (1 << gpio);
break;
case 1:
//result = RPI_GpioBase->GPLEV1 & (1 << (gpio - 32));
result = RPI_GpioBase->GPLEV0[1] & (1 << (gpio - 32));
break;
default:
break;
}
if (result != RPI_IO_UNKNOWN)
{
if (result)
result = RPI_IO_HI;
}
return result;
}
void RPI_ToggleGpio(rpi_gpio_pin_t gpio)
{
if (RPI_GetGpioValue(gpio))
RPI_SetGpioLo(gpio);
else
RPI_SetGpioHi(gpio);
}
void RPI_SetGpioHi(rpi_gpio_pin_t gpio)
{
switch (gpio / 32)
{
case 0:
RPI_GpioBase->GPSET0[0] = (1 << gpio);
break;
case 1:
//RPI_GpioBase->GPSET1 = (1 << (gpio - 32));
RPI_GpioBase->GPSET0[1] = (1 << (gpio - 32));
break;
default:
break;
}
}
void RPI_SetGpioLo(rpi_gpio_pin_t gpio)
{
switch (gpio / 32)
{
case 0:
RPI_GpioBase->GPCLR0[0] = (1 << gpio);
break;
case 1:
//RPI_GpioBase->GPCLR1 = (1 << (gpio - 32));
RPI_GpioBase->GPCLR0[1] = (1 << (gpio - 32));
break;
default:
break;
}
}
void RPI_SetGpioValue(rpi_gpio_pin_t gpio, rpi_gpio_value_t value)
{
if ((value == RPI_IO_LO) || (value == RPI_IO_OFF))
RPI_SetGpioLo(gpio);
else if ((value == RPI_IO_HI) || (value == RPI_IO_ON))
RPI_SetGpioHi(gpio);
}
void EnableGpioDetect(rpi_gpio_pin_t gpio, unsigned type)
{
unsigned mask = (1 << gpio);
unsigned offset = gpio / 32;
switch (type) {
case ARM_GPIO_GPREN0:
RPI_GpioBase->GPREN0[offset] |= mask;
break;
case ARM_GPIO_GPFEN0:
RPI_GpioBase->GPFEN0[offset] |= mask;
break;
case ARM_GPIO_GPHEN0:
RPI_GpioBase->GPHEN0[offset] |= mask;
break;
case ARM_GPIO_GPLEN0:
RPI_GpioBase->GPLEN0[offset] |= mask;
break;
case ARM_GPIO_GPAREN0:
RPI_GpioBase->GPAREN0[offset] |= mask;
break;
case ARM_GPIO_GPAFEN0:
RPI_GpioBase->GPAFEN0[offset] |= mask;
break;
}
}
void DisableGpioDetect(rpi_gpio_pin_t gpio, unsigned type)
{
unsigned mask = ~(1 << (gpio % 32));
unsigned offset = gpio / 32;
switch (type) {
case ARM_GPIO_GPREN0:
RPI_GpioBase->GPREN0[offset] &= mask;
break;
case ARM_GPIO_GPFEN0:
RPI_GpioBase->GPFEN0[offset] &= mask;
break;
case ARM_GPIO_GPHEN0:
RPI_GpioBase->GPHEN0[offset] &= mask;
break;
case ARM_GPIO_GPLEN0:
RPI_GpioBase->GPLEN0[offset] &= mask;
break;
case ARM_GPIO_GPAREN0:
RPI_GpioBase->GPAREN0[offset] &= mask;
break;
case ARM_GPIO_GPAFEN0:
RPI_GpioBase->GPAFEN0[offset] &= mask;
break;
}
}
void ClearGpioEvent(rpi_gpio_pin_t gpio)
{
unsigned mask = ~(1 << (gpio % 32));
unsigned offset = gpio / 32;
RPI_GpioBase->GPEDS0[offset] |= mask;
}
void SetACTLed(int value)
{
#if defined(RPI3)
RPI_GpioVirtSetLed(value);
#endif
}
#if defined(RPI3)
#define MAXIMUM_SUPPORTED_POINTS 10
struct touch_regs
{
uint8_t device_mode;
uint8_t gesture_id;
uint8_t num_points;
struct touch {
uint8_t xh;
uint8_t xl;
uint8_t yh;
uint8_t yl;
uint8_t res1;
uint8_t res2;
} point[MAXIMUM_SUPPORTED_POINTS];
};
static volatile uint32_t touchbuf;
//static uint32_t enables_disables[NUM_GPIO];
void RPI_TouchInit(void)
{
rpi_mailbox_property_t* mp;
RPI_PropertyInit();
RPI_PropertyAddTag(TAG_GET_TOUCHBUF);
RPI_PropertyProcess();
if ((mp = RPI_PropertyGet(TAG_GET_TOUCHBUF)))
touchbuf = mp->data.buffer_32[0] & ~0xC0000000; // Bus to physical
else
touchbuf = 0;
}
void RPI_UpdateTouch(void)
{
struct touch_regs* regs = (struct touch_regs*)touchbuf;
int known_ids = 0;
regs->num_points = MAXIMUM_SUPPORTED_POINTS + 1;
if (!(regs->num_points == (MAXIMUM_SUPPORTED_POINTS + 1) || (regs->num_points == 0 && known_ids == 0)))
{
int i;
int modified_ids = 0, released_ids;
for (i = 0; i < regs->num_points; i++)
{
int x = (((int)regs->point[i].xh & 0xf) << 8) + regs->point[i].xl;
int y = (((int)regs->point[i].yh & 0xf) << 8) + regs->point[i].yl;
int touchid = (regs->point[i].yh >> 4) & 0xf;
modified_ids |= 1 << touchid;
if (!((1 << touchid) & known_ids))
DEBUG_LOG("x = %d, y = %d, touchid = %d\n", x, y, touchid);
}
released_ids = known_ids & ~modified_ids;
for (i = 0; released_ids && i < MAXIMUM_SUPPORTED_POINTS; i++)
{
if (released_ids & (1 << i))
{
DEBUG_LOG("Released %d, known = %x modified = %x\n", i, known_ids, modified_ids);
modified_ids &= ~(1 << i);
}
}
known_ids = modified_ids;
}
}
#define NUM_GPIO 2
static volatile uint32_t gpiovirtbuf;
static uint32_t enables_disables[NUM_GPIO];
uint32_t RPI_GpioVirtGetAddress(void)
{
return gpiovirtbuf;
}
void RPI_GpioVirtInit(void)
{
//int i;
//for (i = 0; i < NUM_GPIO; i++)
//{
// enables_disables[i] = 0;
//}
rpi_mailbox_property_t* mp;
RPI_PropertyInit();
RPI_PropertyAddTag(TAG_GET_GPIOVIRTBUF);
RPI_PropertyProcess();
if ((mp = RPI_PropertyGet(TAG_GET_GPIOVIRTBUF)))
gpiovirtbuf = mp->data.buffer_32[0] & ~0xC0000000; // Bus to physical
else
gpiovirtbuf = 0;
}
// https://www.raspberrypi.org/forums/viewtopic.php?f=72&t=139753
// https://github.com/raspberrypi/firmware/blob/master/extra/dt-blob.dts ln 998
//128 - Bluetooth ON
//129 - WLan power
//130 - LEDS_DISK_ACTIVITY
//131 - LAN_RUN
//132 - HDMI_CONTROL_ATTACHED
//133 - camera power
//134 - camera LED
//135 - POWER_LOW
void RPI_GpioVirtSetLed(int val)
{
//unsigned pin = 0;
//if (gpiovirtbuf == 0)
// return;
//DataMemBarrier();
//if (val)
// write32(gpiovirtbuf + pin * 4, 1 <<16);
//else
// write32(gpiovirtbuf + pin * 4, 1);
uint16_t enables, disables;
int16_t diff;
int lit;
unsigned pin = 0;
//printf("gpiovirtbuf = %08x\r\n", (int)gpiovirtbuf);
if (gpiovirtbuf == 0)
return;
enables = enables_disables[pin] >> 16;
disables = enables_disables[pin] >> 0;
diff = (int16_t)(enables - disables);
lit = diff > 0;
if (!(val ^ lit))
return;
if (val) enables++;
else disables++;
//printf("e = %d d = %d\r\n", enables, disables);
enables_disables[pin] = (enables << 16) | (disables << 0);
DataMemBarrier();
write32(gpiovirtbuf + pin * 4, enables_disables[pin]);
}
#endif

215
rpi-gpio.h Normal file
View File

@ -0,0 +1,215 @@
/*
Part of the Raspberry-Pi Bare Metal Tutorials
Copyright (c) 2013-2015, Brian Sidebotham
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef RPI_GPIO_H
#define RPI_GPIO_H
#include "rpi-base.h"
/** The base address of the GPIO peripheral (ARM Physical Address) */
#define RPI_GPIO_BASE (PERIPHERAL_BASE + 0x200000UL)
//#define GPSET1 (RPI_GPIO_BASE + 0x20)
//#define GPCLR1 (RPI_GPIO_BASE + 0x2c)
#if defined(RPIZERO) || defined(RPIBPLUS) || defined(RPI2) || defined(RPI3)
#define LED_GPIO_BIT 15
#define LED_ON() do { RPI_GpioBase->GPCLR0[1] = (1 << LED_GPIO_BIT); } while(0)
#define LED_OFF() do { RPI_GpioBase->GPSET0[1] = (1 << LED_GPIO_BIT); } while(0)
#elif defined(RPI3)
#else
#define LED_GPIO_BIT 16
#define LED_ON() do { RPI_GpioBase->GPSET0[1] = (1 << LED_GPIO_BIT); } while(0)
#define LED_OFF() do { RPI_GpioBase->GPCLR0[0] = (1 << LED_GPIO_BIT); } while(0)
#endif
typedef enum
{
FS_INPUT = 0,
FS_OUTPUT,
FS_ALT5,
FS_ALT4,
FS_ALT0,
FS_ALT1,
FS_ALT2,
FS_ALT3,
} rpi_gpio_alt_function_t;
/* A mask to be able to clear the bits in the register before setting the value we require */
#define FS_MASK (7)
typedef enum
{
RPI_GPIO0 = 0,
RPI_GPIO1,
RPI_GPIO2,
RPI_GPIO3,
RPI_GPIO4,
RPI_GPIO5,
RPI_GPIO6,
RPI_GPIO7,
RPI_GPIO8,
RPI_GPIO9,
RPI_GPIO10 = 10,
RPI_GPIO11,
RPI_GPIO12,
RPI_GPIO13,
RPI_GPIO14,
RPI_GPIO15,
RPI_GPIO16,
RPI_GPIO17,
RPI_GPIO18,
RPI_GPIO19,
RPI_GPIO20 = 20,
RPI_GPIO21,
RPI_GPIO22,
RPI_GPIO23,
RPI_GPIO24,
RPI_GPIO25,
RPI_GPIO26,
RPI_GPIO27,
RPI_GPIO28,
RPI_GPIO29,
RPI_GPIO30 = 30,
RPI_GPIO31,
RPI_GPIO32,
RPI_GPIO33,
RPI_GPIO34,
RPI_GPIO35,
RPI_GPIO36,
RPI_GPIO37,
RPI_GPIO38,
RPI_GPIO39,
RPI_GPIO40 = 40,
RPI_GPIO41,
RPI_GPIO42,
RPI_GPIO43,
RPI_GPIO44,
RPI_GPIO45,
RPI_GPIO46,
RPI_GPIO47,
RPI_GPIO48,
RPI_GPIO49,
RPI_GPIO50 = 50,
RPI_GPIO51,
RPI_GPIO52,
RPI_GPIO53,
} rpi_gpio_pin_t;
/** The GPIO Peripheral is described in section 6 of the BCM2835 Peripherals
documentation.
There are 54 general-purpose I/O (GPIO) lines split into two banks. All
GPIO pins have at least two alternative functions within BCM. The
alternate functions are usually peripheral IO and a single peripheral
may appear in each bank to allow flexibility on the choice of IO voltage.
Details of alternative functions are given in section 6.2. Alternative
Function Assignments.
The GPIO peripheral has three dedicated interrupt lines. These lines are
triggered by the setting of bits in the event detect status register. Each
bank has its own interrupt line with the third line shared between all
bits.
The Alternate function table also has the pull state (pull-up/pull-down)
which is applied after a power down. */
typedef struct
{
rpi_reg_rw_t GPFSEL[6];
rpi_reg_ro_t Reserved0;
rpi_reg_wo_t GPSET0[2];
//rpi_reg_wo_t GPSET1;
rpi_reg_ro_t Reserved1;
rpi_reg_wo_t GPCLR0[2];
//rpi_reg_wo_t GPCLR1;
rpi_reg_ro_t Reserved2;
rpi_reg_wo_t GPLEV0[2];
//rpi_reg_wo_t GPLEV1;
rpi_reg_ro_t Reserved3;
rpi_reg_wo_t GPEDS0[2];
//rpi_reg_wo_t GPEDS1;
rpi_reg_ro_t Reserved4;
rpi_reg_wo_t GPREN0[2];
//rpi_reg_wo_t GPREN1;
rpi_reg_ro_t Reserved5;
rpi_reg_wo_t GPFEN0[2];
//rpi_reg_wo_t GPFEN1;
rpi_reg_ro_t Reserved6;
rpi_reg_wo_t GPHEN0[2];
//rpi_reg_wo_t GPHEN1;
rpi_reg_ro_t Reserved7;
rpi_reg_wo_t GPLEN0[2];
//rpi_reg_wo_t GPLEN1;
rpi_reg_ro_t Reserved8;
rpi_reg_wo_t GPAREN0[2];
//rpi_reg_wo_t GPAREN1;
rpi_reg_ro_t Reserved9;
rpi_reg_wo_t GPAFEN0[2];
//rpi_reg_wo_t GPAFEN1;
rpi_reg_ro_t Reserved10;
rpi_reg_wo_t GPPUD;
rpi_reg_wo_t GPPUDCLK0;
rpi_reg_wo_t GPPUDCLK1;
rpi_reg_ro_t Reserved11;
} rpi_gpio_t;
typedef enum
{
RPI_IO_LO = 0,
RPI_IO_HI,
RPI_IO_ON,
RPI_IO_OFF,
RPI_IO_UNKNOWN,
} rpi_gpio_value_t;
extern rpi_gpio_t* RPI_GpioBase;
extern void RPI_SetGpioPinFunction(rpi_gpio_pin_t gpio, rpi_gpio_alt_function_t func);
extern void RPI_SetGpioOutput(rpi_gpio_pin_t gpio);
extern void RPI_SetGpioInput(rpi_gpio_pin_t gpio);
extern rpi_gpio_value_t RPI_GetGpioValue(rpi_gpio_pin_t gpio);
extern void RPI_SetGpioHi(rpi_gpio_pin_t gpio);
extern void RPI_SetGpioLo(rpi_gpio_pin_t gpio);
extern void RPI_SetGpioValue(rpi_gpio_pin_t gpio, rpi_gpio_value_t value);
extern void RPI_ToggleGpio(rpi_gpio_pin_t gpio);
extern void EnableGpioDetect(rpi_gpio_pin_t gpio, unsigned type);
extern void DisableGpioDetect(rpi_gpio_pin_t gpio, unsigned type);
extern void ClearGpioEvent(rpi_gpio_pin_t gpio);
extern void SetACTLed(int value);
#if defined(RPI3)
extern void RPI_TouchInit(void);
extern void RPI_UpdateTouch(void);
extern uint32_t RPI_GpioVirtGetAddress(void);
extern void RPI_GpioVirtInit(void);
extern void RPI_GpioVirtSetLed(int value);
#endif
#endif

22
rpi-interrupts.c Normal file
View File

@ -0,0 +1,22 @@
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "startup.h"
#include "rpi-base.h"
#include "rpi-gpio.h"
#include "rpi-interrupts.h"
/** @brief The BCM2835/6 Interupt controller peripheral at it's base address */
static rpi_irq_controller_t* rpiIRQController =
(rpi_irq_controller_t*)RPI_INTERRUPT_CONTROLLER_BASE;
/**
@brief Return the IRQ Controller register set
*/
rpi_irq_controller_t* RPI_GetIrqController( void )
{
return rpiIRQController;
}

74
rpi-interrupts.h Normal file
View File

@ -0,0 +1,74 @@
/*
Part of the Raspberry-Pi Bare Metal Tutorials
Copyright (c) 2013, Brian Sidebotham
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef RPI_INTERRUPTS_H
#define RPI_INTERRUPTS_H
#include <stdint.h>
#include "rpi-base.h"
/** @brief See Section 7.5 of the BCM2836 ARM Peripherals documentation, the base
address of the controller is actually xxxxB000, but there is a 0x200 offset
to the first addressable register for the interrupt controller, so offset the
base to the first register */
#define RPI_INTERRUPT_CONTROLLER_BASE ( PERIPHERAL_BASE + 0xB200 )
/** @brief Bits in the Enable_Basic_IRQs register to enable various interrupts.
See the BCM2835 ARM Peripherals manual, section 7.5 */
#define RPI_BASIC_ARM_TIMER_IRQ (1 << 0)
#define RPI_BASIC_ARM_MAILBOX_IRQ (1 << 1)
#define RPI_BASIC_ARM_DOORBELL_0_IRQ (1 << 2)
#define RPI_BASIC_ARM_DOORBELL_1_IRQ (1 << 3)
#define RPI_BASIC_GPU_0_HALTED_IRQ (1 << 4)
#define RPI_BASIC_GPU_1_HALTED_IRQ (1 << 5)
#define RPI_BASIC_ACCESS_ERROR_1_IRQ (1 << 6)
#define RPI_BASIC_ACCESS_ERROR_0_IRQ (1 << 7)
/** @brief The interrupt controller memory mapped register set */
typedef struct {
volatile uint32_t IRQ_basic_pending;
volatile uint32_t IRQ_pending_1;
volatile uint32_t IRQ_pending_2;
volatile uint32_t FIQ_control;
volatile uint32_t Enable_IRQs_1;
volatile uint32_t Enable_IRQs_2;
volatile uint32_t Enable_Basic_IRQs;
volatile uint32_t Disable_IRQs_1;
volatile uint32_t Disable_IRQs_2;
volatile uint32_t Disable_Basic_IRQs;
} rpi_irq_controller_t;
extern rpi_irq_controller_t* RPI_GetIrqController( void );
extern void reboot_now(void);
#endif

311
rpi-mailbox-interface.c Normal file
View File

@ -0,0 +1,311 @@
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "rpiHardware.h"
#include "rpi-mailbox.h"
#include "rpi-mailbox-interface.h"
/* Make sure the property tag buffer is aligned to a 16-byte boundary because
we only have 28-bits available in the property interface protocol to pass
the address of the buffer to the VC. */
static int pt[PROP_BUFFER_SIZE] __attribute__((aligned(16)));
static int pt_index = 0;
//#define PRINT_PROP_DEBUG 1
void RPI_PropertyInit( void )
{
//memset(pt, 0, sizeof(pt));
/* Fill in the size on-the-fly */
pt[PT_OSIZE] = 12;
/* Process request (All other values are reserved!) */
pt[PT_OREQUEST_OR_RESPONSE] = 0;
/* First available data slot */
pt_index = 2;
/* NULL tag to terminate tag list */
pt[pt_index] = 0;
}
/**
@brief Add a property tag to the current tag list. Data can be included. All data is uint32_t
@param tag
*/
void RPI_PropertyAddTag( rpi_mailbox_tag_t tag, ... )
{
int* ptr;
int value;
va_list vl;
va_start( vl, tag );
pt[pt_index++] = tag;
switch( tag )
{
case TAG_GET_FIRMWARE_VERSION:
case TAG_GET_BOARD_MODEL:
case TAG_GET_BOARD_REVISION:
case TAG_GET_BOARD_MAC_ADDRESS:
case TAG_GET_BOARD_SERIAL:
case TAG_GET_ARM_MEMORY:
case TAG_GET_VC_MEMORY:
case TAG_GET_DMA_CHANNELS:
/* Provide an 8-byte buffer for the response */
pt[pt_index++] = 8;
pt[pt_index++] = 0; /* Request */
pt_index += 2;
break;
case TAG_GET_CLOCKS:
case TAG_GET_COMMAND_LINE:
/* Provide a 1024-byte buffer */
pt[pt_index++] = PROP_SIZE;
pt[pt_index++] = 0; /* Request */
pt_index += PROP_SIZE >> 2;
break;
case TAG_GET_CLOCK_RATE:
case TAG_GET_MAX_CLOCK_RATE:
case TAG_GET_MIN_CLOCK_RATE:
case TAG_GET_TURBO:
case TAG_GET_TEMPERATURE:
case TAG_GET_MAX_TEMPERATURE:
case TAG_GET_VOLTAGE:
case TAG_GET_MIN_VOLTAGE:
case TAG_GET_MAX_VOLTAGE:
pt[pt_index++] = 8;
pt[pt_index++] = 0; /* Request */
pt[pt_index++] = va_arg( vl, int ); /* ClockID */
pt_index += 1;
break;
case TAG_EXECUTE_CODE:
pt[pt_index++] = 28;
pt[pt_index++] = 0; /* Request */
pt[pt_index++] = va_arg( vl, int ); // Function pointer
pt[pt_index++] = va_arg( vl, int ); // R0
pt[pt_index++] = va_arg( vl, int ); // R1
pt[pt_index++] = va_arg( vl, int ); // R2
pt[pt_index++] = va_arg( vl, int ); // R3
pt[pt_index++] = va_arg( vl, int ); // R4
pt[pt_index++] = va_arg( vl, int ); // R5
break;
case TAG_ALLOCATE_BUFFER:
pt[pt_index++] = 8;
pt[pt_index++] = 0; /* Request */
pt[pt_index++] = va_arg( vl, int );
pt_index += 1;
break;
case TAG_GET_PHYSICAL_SIZE:
case TAG_SET_PHYSICAL_SIZE:
case TAG_TEST_PHYSICAL_SIZE:
case TAG_GET_VIRTUAL_SIZE:
case TAG_SET_VIRTUAL_SIZE:
case TAG_TEST_VIRTUAL_SIZE:
case TAG_GET_VIRTUAL_OFFSET:
case TAG_SET_VIRTUAL_OFFSET:
pt[pt_index++] = 8;
pt[pt_index++] = 0; /* Request */
if( ( tag == TAG_SET_PHYSICAL_SIZE ) ||
( tag == TAG_SET_VIRTUAL_SIZE ) ||
( tag == TAG_SET_VIRTUAL_OFFSET ) ||
( tag == TAG_TEST_PHYSICAL_SIZE ) ||
( tag == TAG_TEST_VIRTUAL_SIZE ) )
{
pt[pt_index++] = va_arg( vl, int ); /* Width */
pt[pt_index++] = va_arg( vl, int ); /* Height */
}
else
{
pt_index += 2;
}
break;
case TAG_GET_ALPHA_MODE:
case TAG_SET_ALPHA_MODE:
case TAG_GET_DEPTH:
case TAG_SET_DEPTH:
case TAG_GET_PIXEL_ORDER:
case TAG_SET_PIXEL_ORDER:
case TAG_GET_PITCH:
pt[pt_index++] = 4;
pt[pt_index++] = 0; /* Request */
if( ( tag == TAG_SET_DEPTH ) ||
( tag == TAG_SET_PIXEL_ORDER ) ||
( tag == TAG_SET_ALPHA_MODE ) )
{
/* Colour Depth, bits-per-pixel \ Pixel Order State */
pt[pt_index++] = va_arg( vl, int );
}
else
{
pt_index += 1;
}
break;
case TAG_GET_OVERSCAN:
case TAG_SET_OVERSCAN:
pt[pt_index++] = 16;
pt[pt_index++] = 0; /* Request */
if( ( tag == TAG_SET_OVERSCAN ) )
{
pt[pt_index++] = va_arg( vl, int ); /* Top pixels */
pt[pt_index++] = va_arg( vl, int ); /* Bottom pixels */
pt[pt_index++] = va_arg( vl, int ); /* Left pixels */
pt[pt_index++] = va_arg( vl, int ); /* Right pixels */
}
else
{
pt_index += 4;
}
break;
case TAG_SET_POWER_STATE:
case TAG_GET_POWER_STATE:
pt[pt_index++] = 8;
pt[pt_index++] = 0; /* Request */
if ((tag == TAG_SET_POWER_STATE))
{
pt[pt_index++] = va_arg(vl, int); // deviceID
pt[pt_index++] = va_arg(vl, int); // state
}
else
{
pt_index += 2;
}
pt_index += 1;
break;
case TAG_SET_PALETTE:
ptr = va_arg(vl, int*);
value = ptr[1]; // number of palette entries
pt[pt_index++] = 8 + value * 4;
pt[pt_index++] = 0; /* Request */
pt[pt_index++] = *ptr++; // first palette index
// value = *ptr++; // number of palette entries
ptr++;
pt[pt_index++] = value;
{
for (int index = 0; index < value; ++index)
{
pt[pt_index++] = *ptr++;
}
}
break;
//Set the PWR led signal to output mode, allowing to control it : rpi3 - gpiovirtbuf c 135 1 0 0 0 0
//Set the PWR led off : rpi3 - gpiovirtbuf s 135 0
//GPIO 134 is the camera LED GPIO
//GPIO 133 is the camera power GPIO
case TAG_GET_GPIOVIRTBUF:
case TAG_GET_TOUCHBUF:
pt[pt_index++] = 4;
pt[pt_index++] = 0; /* Request */
pt_index += 1;
break;
default:
/* Unsupported tags, just remove the tag from the list */
pt_index--;
break;
}
/* Make sure the tags are 0 terminated to end the list and update the buffer size */
pt[pt_index] = 0;
va_end( vl );
}
int RPI_PropertyProcess( void )
{
int result;
#if( PRINT_PROP_DEBUG == 1 )
int i;
LOG_INFO( "%s Length: %d\r\n", __func__, pt[PT_OSIZE] );
#endif
/* Fill in the size of the buffer */
pt[PT_OSIZE] = ( pt_index + 1 ) << 2;
pt[PT_OREQUEST_OR_RESPONSE] = 0;
#if( PRINT_PROP_DEBUG == 1 )
for( i = 0; i < (pt[PT_OSIZE] >> 2); i++ )
LOG_INFO( "Request: %3d %8.8X\r\n", i, pt[i] );
#endif
RPI_Mailbox0Write(MB0_TAGS_ARM_TO_VC, (unsigned int)pt);
result = RPI_Mailbox0Read( MB0_TAGS_ARM_TO_VC );
#if( PRINT_PROP_DEBUG == 1 )
for( i = 0; i < (pt[PT_OSIZE] >> 2); i++ )
LOG_INFO( "Response: %3d %8.8X\r\n", i, pt[i] );
#endif
return result;
}
void RPI_PropertyProcessNoCheck( void )
{
#if( PRINT_PROP_DEBUG == 1 )
int i;
LOG_INFO( "%s Length: %d\r\n", __func__, pt[PT_OSIZE] );
#endif
/* Fill in the size of the buffer */
pt[PT_OSIZE] = ( pt_index + 1 ) << 2;
pt[PT_OREQUEST_OR_RESPONSE] = 0;
#if( PRINT_PROP_DEBUG == 1 )
for( i = 0; i < (pt[PT_OSIZE] >> 2); i++ )
LOG_INFO( "Request: %3d %8.8X\r\n", i, pt[i] );
#endif
RPI_Mailbox0Write( MB0_TAGS_ARM_TO_VC, (unsigned int)pt );
}
rpi_mailbox_property_t* RPI_PropertyGet( rpi_mailbox_tag_t tag)
{
static rpi_mailbox_property_t property;
int* tag_buffer = NULL;
property.tag = tag;
/* Get the tag from the buffer. Start at the first tag position */
int index = 2;
while( index < ( pt[PT_OSIZE] >> 2 ) )
{
/* DEBUG_LOG( "Test Tag: [%d] %8.8X\r\n", index, pt[index] ); */
if( pt[index] == tag )
{
tag_buffer = &pt[index];
break;
}
/* Progress to the next tag if we haven't yet discovered the tag */
index += ( pt[index + 1] >> 2 ) + 3;
}
/* Return NULL of the property tag cannot be found in the buffer */
if( tag_buffer == NULL )
return NULL;
/* Return the required data */
property.byte_length = tag_buffer[T_ORESPONSE] & 0xFFFF;
memcpy( property.data.buffer_8, &tag_buffer[T_OVALUE], property.byte_length );
return &property;
}

196
rpi-mailbox-interface.h Normal file
View File

@ -0,0 +1,196 @@
#ifndef RPI_MAILBOX_INTERFACE_H
#define RPI_MAILBOX_INTERFACE_H
#define PROP_BUFFER_SIZE 8192
#define PROP_SIZE 1024
typedef enum
{
POWER_MANAGEMENT = 0,
FRAMEBUFFER,
VIRTUAL_UART,
VCHIQ,
LEDS,
BUTTONS,
TOUCH_SCREEN,
UNDEFINED,
PTAG_ARM_TO_VC,
PTAG_VC_TO_ARM
} Channel;
/**
@brief An enum of the RPI->Videocore firmware mailbox property interface
properties. Further details are available from
https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
Getting out of date now see;-
https://github.com/raspberrypi/firmware/issues/719
*/
typedef enum {
/* Videocore */
TAG_GET_FIRMWARE_VERSION = 0x1,
GET_GPIO_STATE = 0x00030041,
SET_GPIO_STATE = 0x00038041,
/* Hardware */
TAG_GET_BOARD_MODEL = 0x10001,
TAG_GET_BOARD_REVISION,
TAG_GET_BOARD_MAC_ADDRESS,
TAG_GET_BOARD_SERIAL,
TAG_GET_ARM_MEMORY,
TAG_GET_VC_MEMORY,
TAG_GET_CLOCKS,
/* Config */
TAG_GET_COMMAND_LINE = 0x50001,
/* Shared resource management */
TAG_GET_DMA_CHANNELS = 0x60001,
/* Power */
TAG_GET_POWER_STATE = 0x20001,
TAG_GET_TIMING,
TAG_SET_POWER_STATE = 0x28001,
/* Clocks */
TAG_GET_CLOCK_STATE = 0x30001,
TAG_SET_CLOCK_STATE = 0x38001,
TAG_GET_CLOCK_RATE = 0x30002,
TAG_SET_CLOCK_RATE = 0x38002,
TAG_GET_MAX_CLOCK_RATE = 0x30004,
TAG_GET_MIN_CLOCK_RATE = 0x30007,
TAG_GET_TURBO = 0x30009,
TAG_SET_TURBO = 0x38009,
/* Voltage */
TAG_GET_VOLTAGE = 0x30003,
TAG_SET_VOLTAGE = 0x38003,
TAG_GET_MAX_VOLTAGE = 0x30005,
TAG_GET_MIN_VOLTAGE = 0x30008,
TAG_GET_TEMPERATURE = 0x30006,
TAG_GET_MAX_TEMPERATURE = 0x3000A,
TAG_GET_STC = 0x3000B,
TAG_ALLOCATE_MEMORY = 0x3000C,
TAG_LOCK_MEMORY = 0x3000D,
TAG_UNLOCK_MEMORY = 0x3000E,
TAG_RELEASE_MEMORY = 0x3000F,
TAG_EXECUTE_CODE = 0x30010,
TAG_EXECUTE_QPU = 0x30011,
TAG_SET_ENABLE_QPU = 0x30012,
TAG_GET_DISPMANX_MEM_HANDLE = 0x30014,
TAG_GET_EDID_BLOCK = 0x30020,
TAG_GET_CUSTOMER_OTP = 0x30021,
TAG_GET_DOMAIN_STATE = 0x30030,
TAG_GET_GPIO_STATE = 0x30041,
TAG_SET_GPIO_STATE = 0x38041,
TAG_GET_GPIO_CONFIG = 0x30043,
TAG_SET_GPIO_CONFIG = 0x38043,
TAG_SET_CUSTOMER_OTP = 0x38021,
TAG_SET_DOMAIN_STATE = 0x38030,
TAG_SET_SDHOST_CLOCK = 0x38042,
/* Framebuffer */
TAG_ALLOCATE_BUFFER = 0x40001,
TAG_RELEASE_BUFFER = 0x48001,
TAG_BLANK_SCREEN = 0x40002,
TAG_GET_PHYSICAL_SIZE = 0x40003,
TAG_TEST_PHYSICAL_SIZE = 0x44003,
TAG_SET_PHYSICAL_SIZE = 0x48003,
TAG_GET_VIRTUAL_SIZE = 0x40004,
TAG_TEST_VIRTUAL_SIZE = 0x44004,
TAG_SET_VIRTUAL_SIZE = 0x48004,
TAG_GET_DEPTH = 0x40005,
TAG_TEST_DEPTH = 0x44005,
TAG_SET_DEPTH = 0x48005,
TAG_GET_PIXEL_ORDER = 0x40006,
TAG_TEST_PIXEL_ORDER = 0x44006,
TAG_SET_PIXEL_ORDER = 0x48006,
TAG_GET_ALPHA_MODE = 0x40007,
TAG_TEST_ALPHA_MODE = 0x44007,
TAG_SET_ALPHA_MODE = 0x48007,
TAG_GET_PITCH = 0x40008,
TAG_GET_VIRTUAL_OFFSET = 0x40009,
TAG_TEST_VIRTUAL_OFFSET = 0x44009,
TAG_SET_VIRTUAL_OFFSET = 0x48009,
TAG_GET_OVERSCAN = 0x4000A,
TAG_TEST_OVERSCAN = 0x4400A,
TAG_SET_OVERSCAN = 0x4800A,
TAG_GET_PALETTE = 0x4000B,
TAG_TEST_PALETTE = 0x4400B,
TAG_SET_PALETTE = 0x4800B,
TAG_TEST_VSYNC = 0x4400e,
TAG_SET_VSYNC = 0x4800e,
TAG_SET_BACKLIGHT = 0x4800f,
TAG_GET_TOUCHBUF = 0x4000F,
TAG_GET_GPIOVIRTBUF = 0x40010,
TAG_SET_CURSOR_INFO = 0x8011,
TAG_SET_CURSOR_STATE = 0x8010,
TAG_USB = 0xB880
} rpi_mailbox_tag_t;
typedef enum {
TAG_STATE_REQUEST = 0,
TAG_STATE_RESPONSE = 1,
} rpi_tag_state_t;
typedef enum {
PT_OSIZE = 0,
PT_OREQUEST_OR_RESPONSE = 1,
} rpi_tag_buffer_offset_t;
typedef enum {
T_OIDENT = 0,
T_OVALUE_SIZE = 1,
T_ORESPONSE = 2,
T_OVALUE = 3,
} rpi_tag_offset_t;
typedef struct {
int tag;
int byte_length;
union {
int value_32;
unsigned char buffer_8[PROP_SIZE];
int buffer_32[PROP_SIZE >> 2];
} data;
} rpi_mailbox_property_t;
/* Clock ID values */
#define RES_CLK_ID 0x000000000
#define EMMC_CLK_ID 0x000000001
#define UART_CLK_ID 0x000000002
#define ARM_CLK_ID 0x000000003
#define CORE_CLK_ID 0x000000004
#define V3D_CLK_ID 0x000000005
#define H264_CLK_ID 0x000000006
#define ISP_CLK_ID 0x000000007
#define SDRAM_CLK_ID 0x000000008
#define PIXEL_CLK_ID 0x000000009
#define PWM_CLK_ID 0x00000000a
#define MIN_CLK_ID 0x000000001
#define MAX_CLK_ID 0x00000000a
extern void RPI_PropertyInit( void );
extern void RPI_PropertyAddTag( rpi_mailbox_tag_t tag, ... );
extern int RPI_PropertyProcess( void );
extern void RPI_PropertyProcessNoCheck( void );
extern rpi_mailbox_property_t* RPI_PropertyGet( rpi_mailbox_tag_t tag );
//extern void SetACTLED(int value);
#endif

58
rpi-mailbox.c Normal file
View File

@ -0,0 +1,58 @@
#include <stdint.h>
#include "rpi-gpio.h"
#include "rpi-mailbox.h"
extern void usDelay(unsigned nMicroSeconds);
/* Mailbox 0 mapped to it's base address */
static mailbox_t* rpiMailbox0 = (mailbox_t*)RPI_MAILBOX0_BASE;
void RPI_Mailbox0Write( mailbox0_channel_t channel, int value )
{
/* For information about accessing mailboxes, see:
https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes */
/* Add the channel number into the lower 4 bits */
value &= ~(0xF);
value |= channel;
/* Wait until the mailbox becomes available and then write to the mailbox
channel */
while( ( rpiMailbox0->Status & ARM_MS_FULL ) != 0 ) { }
/* Write the modified value + channel number into the write register */
rpiMailbox0->Write = value;
}
int RPI_Mailbox0Read( mailbox0_channel_t channel )
{
/* For information about accessing mailboxes, see:
https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes */
int value = -1;
/* Keep reading the register until the desired channel gives us a value */
while( ( value & 0xF ) != channel )
{
/* Wait while the mailbox is empty because otherwise there's no value
to read! */
while( rpiMailbox0->Status & ARM_MS_EMPTY ) { }
/* Extract the value from the Read register of the mailbox. The value
is actually in the upper 28 bits */
value = rpiMailbox0->Read;
}
/* Return just the value (the upper 28-bits) */
return value >> 4;
}
void RPI_Mailbox0Flush(void)
{
while (!(rpiMailbox0->Status & ARM_MS_EMPTY))
{
(void)rpiMailbox0->Read;
usDelay(20);
}
}

48
rpi-mailbox.h Normal file
View File

@ -0,0 +1,48 @@
#ifndef RPI_MAILBOX_H
#define RPI_MAILBOX_H
#include "rpi-base.h"
#define RPI_MAILBOX0_BASE ( PERIPHERAL_BASE + 0xB880 )
/* The available mailbox channels in the BCM2835 Mailbox interface.
See https://github.com/raspberrypi/firmware/wiki/Mailboxes for
information */
typedef enum {
MB0_POWER_MANAGEMENT = 0,
MB0_FRAMEBUFFER,
MB0_VIRTUAL_UART,
MB0_VCHIQ,
MB0_LEDS,
MB0_BUTTONS,
MB0_TOUCHSCREEN,
MB0_UNUSED,
MB0_TAGS_ARM_TO_VC,
MB0_TAGS_VC_TO_ARM,
} mailbox0_channel_t;
/* These defines come from the Broadcom Videocode driver source code, see:
brcm_usrlib/dag/vmcsx/vcinclude/bcm2708_chip/arm_control.h */
enum mailbox_status_reg_bits {
ARM_MS_FULL = 0x80000000,
ARM_MS_EMPTY = 0x40000000,
ARM_MS_LEVEL = 0x400000FF,
};
/* Define a structure which defines the register access to a mailbox.
Not all mailboxes support the full register set! */
typedef struct {
volatile unsigned int Read;
volatile unsigned int reserved1[((0x90 - 0x80) / 4) - 1];
volatile unsigned int Poll;
volatile unsigned int Sender;
volatile unsigned int Status;
volatile unsigned int Configuration;
volatile unsigned int Write;
} mailbox_t;
extern void RPI_Mailbox0Write( mailbox0_channel_t channel, int value );
extern int RPI_Mailbox0Read( mailbox0_channel_t channel );
extern void RPI_Mailbox0Flush(void);
#endif

206
rpiHardware.h Normal file
View File

@ -0,0 +1,206 @@
// Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White
//
// This file is part of Pi1541.
//
// Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#ifndef RPIHARDWARE_H
#define RPIHARDWARE_H
#include <stdio.h>
#include "types.h"
#include "rpi-gpio.h"
#include "debug.h"
#define DMA_ENABLE (PERIPHERAL_BASE + 0x7FF0) // Global Enable bits for each DMA Channel
#define DMA0_BASE (PERIPHERAL_BASE + 0x7000) // DMA Channel 0 Register Set
#define DMA_CONBLK_AD 4 // DMA Channel 0..14 Control Block Address
#define DMA_CS 0 // DMA Channel 0..14 Control & Status
#define DMA_ACTIVE 1
#define DMA_END 2
#define DMA_DEST_DREQ 0x40
#define DMA_SRC_INC 0x100
#define DMA_PERMAP_5 0x50000
#define ARM_GPIO_GPFSEL0 (RPI_GPIO_BASE + 0x00)
#define ARM_GPIO_GPFSEL1 (RPI_GPIO_BASE + 0x04)
#define ARM_GPIO_GPFSEL4 (RPI_GPIO_BASE + 0x10)
#define ARM_GPIO_GPSET0 (RPI_GPIO_BASE + 0x1C)
#define ARM_GPIO_GPCLR0 (RPI_GPIO_BASE + 0x28)
#define ARM_GPIO_GPLEV0 (RPI_GPIO_BASE + 0x34)
#define ARM_GPIO_GPEDS0 (RPI_GPIO_BASE + 0x40)
#define ARM_GPIO_GPREN0 (RPI_GPIO_BASE + 0x4C)
#define ARM_GPIO_GPFEN0 (RPI_GPIO_BASE + 0x58)
#define ARM_GPIO_GPHEN0 (RPI_GPIO_BASE + 0x64)
#define ARM_GPIO_GPLEN0 (RPI_GPIO_BASE + 0x70)
#define ARM_GPIO_GPAREN0 (RPI_GPIO_BASE + 0x7C)
#define ARM_GPIO_GPAFEN0 (RPI_GPIO_BASE + 0x88)
#define ARM_GPIO_GPPUD (RPI_GPIO_BASE + 0x94)
#define ARM_GPIO_GPPUDCLK0 (RPI_GPIO_BASE + 0x98)
#define ARM_SYSTIMER_BASE (PERIPHERAL_BASE + 0x3000)
#define ARM_SYSTIMER_CS (ARM_SYSTIMER_BASE + 0x00)
#define ARM_SYSTIMER_CLO (ARM_SYSTIMER_BASE + 0x04)
#define ARM_SYSTIMER_CHI (ARM_SYSTIMER_BASE + 0x08)
#define ARM_SYSTIMER_C0 (ARM_SYSTIMER_BASE + 0x0C)
#define ARM_SYSTIMER_C1 (ARM_SYSTIMER_BASE + 0x10)
#define ARM_SYSTIMER_C2 (ARM_SYSTIMER_BASE + 0x14)
#define ARM_SYSTIMER_C3 (ARM_SYSTIMER_BASE + 0x18)
// External Mass Media Controller (SD Card)
#define ARM_EMMC_BASE (PERIPHERAL_BASE + 0x300000)
#define DEVICE_ID_SD_CARD 0
#define DEVICE_ID_USB_HCD 3
#define POWER_STATE_OFF (0 << 0)
#define POWER_STATE_ON (1 << 0)
#define POWER_STATE_WAIT (1 << 1)
#define POWER_STATE_DEVICE_DOESNT_EXIST (1 << 1) // in response
#define CLOCK_ID_EMMC 1
#define CLOCK_ID_UART 2
#define CLOCK_ID_CORE 4
#define CM_BASE (PERIPHERAL_BASE + 0x101000)
#define CM_PWMCTL (CM_BASE + 0xA0) // Clock Manager PWM Clock Control
#define CM_PWMDIV (CM_BASE + 0xA4) // Clock Manager PWM Clock Divisor
#define CM_PASSWORD 0x5A000000 // Clock Control Password "5A"
#define CM_SRC_OSCILLATOR 0x01
#define CM_ENAB 0x10 // Enable The Clock Generator
#define PWM_BASE (PERIPHERAL_BASE + 0x20C000)
#define PWM_CTL (PWM_BASE + 0x0)
#define PWM_STATUS (PWM_BASE + 0x4)
#define PWM_DMAC (PWM_BASE + 0x8)
#define PWM_RNG1 (PWM_BASE + 0x10)
#define PWM_FIF1 (PWM_BASE + 0x18) // PWM FIFO Input
#define PWM_RNG2 (PWM_BASE + 0x20)
#define PWM_ENAB 0x80000000 // PWM DMA Configuration DMA Enable
#define PWM_PWEN1 0x1 // Channel 1 Enable
#define PWM_MODE1 0x2 // Channel 1 Mode
#define PWM_RPTL1 0x4 // Channel 1 Repeat Last Data
#define PWM_SBIT1 0x8 // Channel 1 Silence Bit
#define PWM_POLA1 0x10 // Channel 1 Polarity
#define PWM_USEF1 0x20 // Channel 1 Use Fifo
#define PWM_CLRF1 0x40 // Clear Fifo
#define PWM_MSEN1 0x80 // Channel 1 M / S Enable
#define PWM_PWEN2 0x100 // Channel 2 Enable
#define PWM_MODE2 0x200 // Channel 2 Mode
#define PWM_RPTL2 0x400 // Channel 2 Repeat Last Data
#define PWM_SBIT2 0x800 // Channel 2 Silence Bit
#define PWM_POLA2 0x1000 // Channel 2 Polarity
#define PWM_USEF2 0x2000 // Channel 2 Use Fifo
#define PWM_MSEN2 0x8000 // Channel 2 M / S Enable
// PWM Status flags
#define PWM_FULL1 0x1
#define PWM_EMPT1 0x2
//
// Interrupt Controller
//
#define ARM_IC_BASE (PERIPHERAL_BASE + 0xB000)
#define ARM_IC_IRQ_BASIC_PENDING (ARM_IC_BASE + 0x200)
#define ARM_IC_IRQ_PENDING_1 (ARM_IC_BASE + 0x204)
#define ARM_IC_IRQ_PENDING_2 (ARM_IC_BASE + 0x208)
#define ARM_IC_FIQ_CONTROL (ARM_IC_BASE + 0x20C)
#define ARM_IC_ENABLE_IRQS_1 (ARM_IC_BASE + 0x210)
#define ARM_IC_ENABLE_IRQS_2 (ARM_IC_BASE + 0x214)
#define ARM_IC_ENABLE_BASIC_IRQS (ARM_IC_BASE + 0x218)
#define ARM_IC_DISABLE_IRQS_1 (ARM_IC_BASE + 0x21C)
#define ARM_IC_DISABLE_IRQS_2 (ARM_IC_BASE + 0x220)
#define ARM_IC_DISABLE_BASIC_IRQS (ARM_IC_BASE + 0x224)
#ifdef __cplusplus
extern "C" {
#endif
#include "rpi-mailbox-interface.h"
static inline u32 read32(unsigned int nAddress)
{
return *(u32 volatile *)nAddress;
}
static inline void write32(unsigned int nAddress, u32 nValue)
{
*(u32 volatile *)nAddress = nValue;
}
static inline void delay_us(u32 amount)
{
u32 count;
for (count = 0; count < amount; ++count)
{
unsigned before;
unsigned after;
// We try to update every micro second and use as a rough timer to count micro seconds
before = read32(ARM_SYSTIMER_CLO);
do
{
after = read32(ARM_SYSTIMER_CLO);
} while (after == before);
}
}
static inline int get_clock_rate(int clk_id)
{
rpi_mailbox_property_t *buf;
RPI_PropertyInit();
RPI_PropertyAddTag(TAG_GET_CLOCK_RATE, clk_id);
RPI_PropertyProcess();
buf = RPI_PropertyGet(TAG_GET_CLOCK_RATE);
if (buf)
return buf->data.buffer_32[1];
else
return 0;
}
#ifdef __cplusplus
}
#endif
//DMB - whenever a memory access requires ordering with regards to another memory access.
//DSB - whenever a memory access needs to have completed before program execution progresses.
//ISB - whenever instruction fetches need to explicitly take place after a certain point in the program, for example after memory map updates or after writing code to be executed. (In practice, this means "throw away any prefetched instructions at this point".)
//DMB - It prevents reordering of data accesses instructions across itself. All data accesses by this processor / core before the DMB will be visible to all other masters within the specified shareability domain before any of the data accesses after it.
// It also ensures that any explicit preceding data(or unified) cache maintenance operations have completed before any subsequent data accesses are executed.
#if defined(RPI2) || defined(RPI3)
#define DataSyncBarrier() asm volatile ("dsb" ::: "memory")
#define DataMemBarrier() asm volatile ("dmb" ::: "memory")
#define InstructionSyncBarrier() __asm volatile ("isb" ::: "memory")
#define InstructionMemBarrier() __asm volatile ("isb" ::: "memory")
#else
#define DataSyncBarrier() asm volatile ("mcr p15, 0, %0, c7, c10, 4" : : "r" (0) : "memory")
#define DataMemBarrier() asm volatile ("mcr p15, 0, %0, c7, c10, 5" : : "r" (0) : "memory")
#define FlushPrefetchBuffer() __asm volatile ("mcr p15, 0, %0, c7, c5, 4" : : "r" (0) : "memory")
#define InstructionSyncBarrier() FlushPrefetchBuffer()
#define InstructionMemBarrier() FlushPrefetchBuffer()
#endif
#endif

830
sample.h Normal file
View File

@ -0,0 +1,830 @@
/* Generated by bin2c, do not edit manually */
/* Contents of file Sample.bin */
const long int Sample_bin_size = 13226-48;
const unsigned char Sample_bin[13226-48] = {
0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x82, 0x82, 0x82, 0x81,
0x81, 0x82, 0x82, 0x82, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, 0x7F, 0x7F, 0x7E, 0x7E, 0x7D, 0x7E,
0x7E, 0x7D, 0x7C, 0x7B, 0x7C, 0x7D, 0x7D, 0x7D, 0x7D, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x80, 0x81,
0x81, 0x81, 0x81, 0x81, 0x81, 0x7F, 0x7F, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,
0x7F, 0x7E, 0x7E, 0x7D, 0x7E, 0x7F, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7E, 0x7E, 0x7D, 0x7D,
0x7C, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7F, 0x81, 0x82, 0x82, 0x83, 0x83, 0x83, 0x84, 0x84, 0x83,
0x82, 0x81, 0x81, 0x81, 0x80, 0x7F, 0x80, 0x81, 0x82, 0x82, 0x82, 0x81, 0x81, 0x81, 0x81, 0x80,
0x7F, 0x7E, 0x7E, 0x7F, 0x7F, 0x7D, 0x7C, 0x7B, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, 0x7E, 0x7F, 0x80,
0x80, 0x81, 0x81, 0x80, 0x80, 0x80, 0x7F, 0x7E, 0x7D, 0x7E, 0x7E, 0x7E, 0x7D, 0x7D, 0x7D, 0x7F,
0x80, 0x81, 0x81, 0x81, 0x83, 0x84, 0x84, 0x84, 0x84, 0x83, 0x82, 0x82, 0x81, 0x81, 0x7F, 0x7E,
0x7E, 0x80, 0x80, 0x7F, 0x7C, 0x7B, 0x7B, 0x7C, 0x7E, 0x80, 0x83, 0x84, 0x82, 0x82, 0x82, 0x83,
0x83, 0x81, 0x80, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7E, 0x7F, 0x82, 0x84, 0x84, 0x84,
0x84, 0x83, 0x81, 0x7D, 0x7A, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x7E, 0x7E, 0x7D, 0x7B, 0x7B,
0x7C, 0x7E, 0x81, 0x84, 0x84, 0x81, 0x7E, 0x7C, 0x7D, 0x7E, 0x7F, 0x7E, 0x7F, 0x7F, 0x7F, 0x7B,
0x78, 0x76, 0x75, 0x74, 0x73, 0x74, 0x77, 0x7A, 0x7C, 0x7E, 0x80, 0x84, 0x86, 0x87, 0x88, 0x89,
0x89, 0x89, 0x8A, 0x8B, 0x89, 0x86, 0x82, 0x81, 0x82, 0x83, 0x84, 0x84, 0x83, 0x81, 0x7F, 0x81,
0x85, 0x89, 0x8A, 0x89, 0x88, 0x89, 0x8B, 0x8B, 0x89, 0x84, 0x7F, 0x7B, 0x78, 0x74, 0x6F, 0x6A,
0x66, 0x65, 0x65, 0x66, 0x68, 0x6A, 0x6E, 0x73, 0x7B, 0x83, 0x8A, 0x8F, 0x94, 0x97, 0x98, 0x94,
0x90, 0x8D, 0x8C, 0x8A, 0x85, 0x7F, 0x7C, 0x7E, 0x81, 0x82, 0x7F, 0x7C, 0x7D, 0x80, 0x85, 0x89,
0x8D, 0x8D, 0x87, 0x7F, 0x78, 0x76, 0x74, 0x6F, 0x68, 0x62, 0x5F, 0x5E, 0x5C, 0x5B, 0x5F, 0x6A,
0x77, 0x7F, 0x86, 0x8F, 0x99, 0xA2, 0xA6, 0xA8, 0xAA, 0xAA, 0xA7, 0xA2, 0x9C, 0x96, 0x90, 0x8C,
0x8A, 0x88, 0x83, 0x7C, 0x74, 0x6F, 0x6E, 0x71, 0x78, 0x7F, 0x83, 0x80, 0x77, 0x6E, 0x6A, 0x69,
0x69, 0x68, 0x66, 0x64, 0x64, 0x69, 0x71, 0x78, 0x7C, 0x7E, 0x7F, 0x80, 0x81, 0x83, 0x87, 0x88,
0x88, 0x8A, 0x8C, 0x8D, 0x8B, 0x88, 0x89, 0x8E, 0x90, 0x90, 0x8F, 0x8F, 0x8C, 0x82, 0x78, 0x72,
0x71, 0x6F, 0x6A, 0x64, 0x63, 0x65, 0x6A, 0x71, 0x78, 0x7C, 0x7D, 0x7D, 0x7F, 0x7F, 0x7D, 0x7B,
0x7A, 0x7A, 0x7A, 0x7C, 0x7F, 0x81, 0x83, 0x87, 0x8F, 0x97, 0x9D, 0xA3, 0xA8, 0xA9, 0xA5, 0x9D,
0x97, 0x91, 0x89, 0x7C, 0x70, 0x69, 0x65, 0x63, 0x67, 0x72, 0x7C, 0x7D, 0x76, 0x71, 0x72, 0x72,
0x70, 0x71, 0x79, 0x86, 0x90, 0x96, 0x99, 0x99, 0x8E, 0x79, 0x64, 0x58, 0x58, 0x5B, 0x5D, 0x62,
0x69, 0x74, 0x7F, 0x8C, 0x9D, 0xAE, 0xBB, 0xC2, 0xC4, 0xC0, 0xB4, 0xA2, 0x92, 0x81, 0x6C, 0x57,
0x4A, 0x44, 0x3E, 0x36, 0x30, 0x30, 0x3C, 0x59, 0x7F, 0x9D, 0xAC, 0xB2, 0xB8, 0xB9, 0xB1, 0xA1,
0x94, 0x8A, 0x80, 0x75, 0x70, 0x70, 0x6D, 0x64, 0x60, 0x65, 0x6B, 0x72, 0x80, 0x9A, 0xB5, 0xC4,
0xC8, 0xCC, 0xD5, 0xD8, 0xCC, 0xB2, 0x98, 0x82, 0x6B, 0x51, 0x3D, 0x33, 0x2C, 0x22, 0x17, 0x17,
0x25, 0x3B, 0x50, 0x61, 0x79, 0x9B, 0xBD, 0xCF, 0xD0, 0xCA, 0xC2, 0xB4, 0x99, 0x76, 0x5B, 0x4F,
0x4B, 0x49, 0x48, 0x4D, 0x5A, 0x68, 0x70, 0x71, 0x72, 0x7F, 0x96, 0xAD, 0xBF, 0xCE, 0xDB, 0xDF,
0xD7, 0xC7, 0xB2, 0x97, 0x79, 0x62, 0x57, 0x54, 0x55, 0x5A, 0x65, 0x6E, 0x71, 0x6E, 0x6C, 0x6A,
0x67, 0x66, 0x6A, 0x70, 0x71, 0x6B, 0x67, 0x69, 0x6A, 0x65, 0x5D, 0x58, 0x59, 0x5F, 0x69, 0x79,
0x8C, 0xA0, 0xB1, 0xBB, 0xBE, 0xB9, 0xB2, 0xB0, 0xB4, 0xBB, 0xBF, 0xC2, 0xC2, 0xBC, 0xAB, 0x91,
0x77, 0x62, 0x57, 0x52, 0x52, 0x55, 0x5C, 0x64, 0x69, 0x63, 0x56, 0x4A, 0x48, 0x4F, 0x58, 0x61,
0x69, 0x71, 0x78, 0x7A, 0x75, 0x6A, 0x5D, 0x55, 0x54, 0x5A, 0x66, 0x76, 0x85, 0x8A, 0x88, 0x87,
0x8D, 0x98, 0x9F, 0xA1, 0xA1, 0xA1, 0xA2, 0xA7, 0xB2, 0xBF, 0xC1, 0xB5, 0xA3, 0x93, 0x88, 0x81,
0x83, 0x8E, 0x99, 0x9E, 0x9C, 0x9B, 0x9B, 0x9A, 0x95, 0x8F, 0x87, 0x7C, 0x70, 0x68, 0x62, 0x5C,
0x59, 0x5B, 0x60, 0x5E, 0x56, 0x52, 0x55, 0x5B, 0x61, 0x6F, 0x8B, 0xAB, 0xBA, 0xB2, 0x9E, 0x8C,
0x7F, 0x78, 0x76, 0x7D, 0x8C, 0x9D, 0xA4, 0x9F, 0x90, 0x7D, 0x68, 0x56, 0x4A, 0x49, 0x50, 0x60,
0x73, 0x86, 0x8F, 0x89, 0x7B, 0x6B, 0x5E, 0x58, 0x5B, 0x63, 0x68, 0x69, 0x6B, 0x71, 0x77, 0x7F,
0x8F, 0xA7, 0xB8, 0xBA, 0xB8, 0xBB, 0xC2, 0xC3, 0xBC, 0xB1, 0xA9, 0xA3, 0x9B, 0x8E, 0x7A, 0x63,
0x4D, 0x40, 0x3D, 0x47, 0x5A, 0x70, 0x80, 0x8A, 0x95, 0xA1, 0xA9, 0xAB, 0xA8, 0xA4, 0x9E, 0x9A,
0x97, 0x92, 0x83, 0x68, 0x45, 0x24, 0x10, 0x0F, 0x22, 0x40, 0x5C, 0x71, 0x83, 0x92, 0x98, 0x95,
0x93, 0x9B, 0xA8, 0xB1, 0xAD, 0xA0, 0x92, 0x84, 0x76, 0x65, 0x55, 0x50, 0x58, 0x68, 0x7A, 0x8B,
0x9A, 0xA4, 0xA7, 0xA5, 0xA0, 0x96, 0x84, 0x6E, 0x5C, 0x53, 0x4E, 0x47, 0x3B, 0x31, 0x2E, 0x32,
0x38, 0x3F, 0x4D, 0x61, 0x77, 0x8C, 0xA2, 0xBC, 0xD6, 0xEA, 0xF5, 0xFC, 0xFF, 0xF9, 0xE3, 0xC3,
0xA4, 0x8C, 0x79, 0x6A, 0x62, 0x62, 0x62, 0x5A, 0x4C, 0x3F, 0x3C, 0x44, 0x53, 0x64, 0x74, 0x7E,
0x80, 0x7C, 0x76, 0x6F, 0x66, 0x5D, 0x59, 0x5D, 0x68, 0x73, 0x7B, 0x80, 0x81, 0x7F, 0x7A, 0x76,
0x77, 0x7D, 0x82, 0x87, 0x8C, 0x92, 0x98, 0x9D, 0x9E, 0x9B, 0x97, 0x94, 0x92, 0x91, 0x93, 0x97,
0x9C, 0x9D, 0x9B, 0x98, 0x97, 0x97, 0x96, 0x94, 0x90, 0x8D, 0x8A, 0x86, 0x7F, 0x75, 0x6B, 0x62,
0x5B, 0x53, 0x4B, 0x44, 0x40, 0x40, 0x44, 0x4C, 0x56, 0x62, 0x6D, 0x77, 0x7B, 0x7D, 0x7F, 0x83,
0x89, 0x90, 0x96, 0x9C, 0xA3, 0xAA, 0xAD, 0xAE, 0xAF, 0xB4, 0xBD, 0xC5, 0xC9, 0xC6, 0xBF, 0xB5,
0xAB, 0xA2, 0x98, 0x8C, 0x7E, 0x70, 0x63, 0x59, 0x53, 0x50, 0x50, 0x54, 0x5B, 0x65, 0x6D, 0x74,
0x77, 0x7A, 0x7D, 0x81, 0x87, 0x8D, 0x8F, 0x8D, 0x87, 0x7F, 0x73, 0x67, 0x5C, 0x57, 0x54, 0x51,
0x4E, 0x4F, 0x53, 0x5A, 0x62, 0x69, 0x6E, 0x75, 0x83, 0x95, 0xA7, 0xB3, 0xB8, 0xBB, 0xB9, 0xB3,
0xA5, 0x95, 0x86, 0x78, 0x6C, 0x62, 0x5B, 0x59, 0x5D, 0x66, 0x73, 0x83, 0x94, 0xA2, 0xAC, 0xB5,
0xBC, 0xC1, 0xC1, 0xBC, 0xB3, 0xA9, 0x9E, 0x93, 0x88, 0x7C, 0x70, 0x65, 0x5B, 0x54, 0x52, 0x56,
0x5E, 0x67, 0x6E, 0x71, 0x72, 0x72, 0x73, 0x77, 0x7B, 0x7D, 0x7C, 0x79, 0x77, 0x76, 0x73, 0x6E,
0x65, 0x5C, 0x55, 0x51, 0x51, 0x55, 0x5C, 0x62, 0x67, 0x6A, 0x70, 0x7A, 0x89, 0x97, 0xA1, 0xA5,
0xA5, 0xA5, 0xA4, 0xA1, 0x9B, 0x93, 0x8A, 0x83, 0x7F, 0x7F, 0x80, 0x80, 0x7D, 0x79, 0x76, 0x76,
0x75, 0x71, 0x6B, 0x66, 0x66, 0x6C, 0x75, 0x7F, 0x88, 0x90, 0x96, 0x98, 0x97, 0x95, 0x95, 0x98,
0x9A, 0x98, 0x90, 0x84, 0x7A, 0x74, 0x73, 0x73, 0x72, 0x70, 0x6E, 0x6E, 0x71, 0x75, 0x7B, 0x80,
0x85, 0x88, 0x8B, 0x8C, 0x8D, 0x8D, 0x8E, 0x8F, 0x90, 0x90, 0x8E, 0x8B, 0x87, 0x82, 0x7D, 0x78,
0x73, 0x6C, 0x64, 0x5B, 0x52, 0x4A, 0x46, 0x48, 0x51, 0x5C, 0x66, 0x6D, 0x74, 0x7B, 0x84, 0x8D,
0x92, 0x94, 0x93, 0x92, 0x92, 0x91, 0x8E, 0x8C, 0x8C, 0x8F, 0x91, 0x92, 0x92, 0x94, 0x98, 0x9B,
0x99, 0x94, 0x8F, 0x8D, 0x8C, 0x8B, 0x8A, 0x8A, 0x8B, 0x8A, 0x85, 0x7F, 0x7B, 0x7A, 0x7A, 0x7B,
0x7B, 0x7E, 0x81, 0x85, 0x86, 0x86, 0x85, 0x84, 0x83, 0x80, 0x7E, 0x7A, 0x76, 0x71, 0x6D, 0x6A,
0x69, 0x6B, 0x70, 0x74, 0x76, 0x76, 0x7A, 0x80, 0x88, 0x8D, 0x91, 0x97, 0x9E, 0xA1, 0xA1, 0x9E,
0x99, 0x93, 0x8C, 0x84, 0x7B, 0x70, 0x65, 0x5C, 0x56, 0x53, 0x52, 0x54, 0x57, 0x5C, 0x60, 0x64,
0x68, 0x6C, 0x72, 0x7B, 0x86, 0x91, 0x98, 0x9B, 0x9C, 0x9D, 0x9E, 0x9C, 0x99, 0x96, 0x95, 0x97,
0x99, 0x9A, 0x99, 0x98, 0x97, 0x97, 0x95, 0x91, 0x8D, 0x8A, 0x87, 0x82, 0x7B, 0x74, 0x6E, 0x6A,
0x68, 0x67, 0x6A, 0x6E, 0x74, 0x79, 0x7B, 0x7B, 0x7A, 0x79, 0x79, 0x7C, 0x80, 0x82, 0x80, 0x7D,
0x7A, 0x77, 0x74, 0x70, 0x6D, 0x6C, 0x6E, 0x72, 0x75, 0x78, 0x7B, 0x7F, 0x82, 0x84, 0x86, 0x88,
0x8C, 0x8F, 0x91, 0x8F, 0x8B, 0x86, 0x81, 0x7A, 0x72, 0x6A, 0x66, 0x63, 0x62, 0x60, 0x5D, 0x5C,
0x5C, 0x5D, 0x60, 0x64, 0x6B, 0x75, 0x7F, 0x87, 0x8D, 0x91, 0x93, 0x92, 0x90, 0x8C, 0x89, 0x88,
0x89, 0x8A, 0x8B, 0x8E, 0x93, 0x98, 0x9A, 0x9D, 0xA0, 0xA5, 0xA7, 0xA7, 0xA5, 0xA2, 0x9F, 0x9B,
0x94, 0x8D, 0x85, 0x7D, 0x76, 0x70, 0x6D, 0x6B, 0x6A, 0x6A, 0x6B, 0x6D, 0x70, 0x73, 0x73, 0x71,
0x6D, 0x6B, 0x6C, 0x6B, 0x69, 0x66, 0x67, 0x6C, 0x72, 0x77, 0x7A, 0x7C, 0x80, 0x83, 0x84, 0x85,
0x88, 0x8D, 0x91, 0x90, 0x8D, 0x89, 0x86, 0x83, 0x7F, 0x7B, 0x78, 0x77, 0x77, 0x77, 0x79, 0x7D,
0x81, 0x84, 0x86, 0x87, 0x89, 0x8B, 0x8D, 0x8F, 0x8F, 0x8E, 0x8C, 0x8A, 0x88, 0x86, 0x85, 0x83,
0x82, 0x81, 0x80, 0x7F, 0x7F, 0x7E, 0x7D, 0x7A, 0x78, 0x76, 0x76, 0x77, 0x7A, 0x7E, 0x83, 0x87,
0x8A, 0x8E, 0x90, 0x91, 0x90, 0x8D, 0x8A, 0x87, 0x85, 0x85, 0x84, 0x84, 0x84, 0x81, 0x7E, 0x7A,
0x77, 0x75, 0x73, 0x71, 0x6F, 0x6F, 0x70, 0x73, 0x74, 0x75, 0x75, 0x75, 0x76, 0x77, 0x7A, 0x7E,
0x82, 0x85, 0x87, 0x88, 0x8A, 0x8C, 0x8D, 0x8B, 0x87, 0x81, 0x7D, 0x7A, 0x79, 0x78, 0x79, 0x79,
0x7A, 0x7C, 0x7F, 0x82, 0x85, 0x86, 0x87, 0x88, 0x8A, 0x8B, 0x8C, 0x8C, 0x8C, 0x8B, 0x89, 0x88,
0x87, 0x87, 0x88, 0x88, 0x86, 0x84, 0x82, 0x80, 0x7D, 0x7A, 0x76, 0x72, 0x70, 0x6E, 0x6E, 0x70,
0x73, 0x75, 0x76, 0x75, 0x76, 0x77, 0x7A, 0x7C, 0x7C, 0x7B, 0x7B, 0x7B, 0x7C, 0x7D, 0x7D, 0x7D,
0x7E, 0x7E, 0x7F, 0x7F, 0x80, 0x81, 0x81, 0x81, 0x80, 0x7F, 0x7E, 0x7E, 0x7D, 0x7D, 0x7D, 0x7E,
0x7F, 0x80, 0x80, 0x81, 0x82, 0x84, 0x85, 0x85, 0x86, 0x85, 0x86, 0x86, 0x85, 0x84, 0x83, 0x83,
0x81, 0x7E, 0x7B, 0x7A, 0x79, 0x78, 0x78, 0x79, 0x7B, 0x7E, 0x82, 0x85, 0x88, 0x8A, 0x8D, 0x8F,
0x92, 0x95, 0x95, 0x93, 0x8F, 0x8A, 0x86, 0x83, 0x81, 0x7E, 0x79, 0x75, 0x71, 0x6F, 0x6E, 0x6D,
0x6D, 0x6E, 0x70, 0x72, 0x75, 0x77, 0x79, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x79, 0x78, 0x76, 0x75,
0x73, 0x73, 0x74, 0x76, 0x78, 0x7A, 0x7B, 0x7B, 0x7C, 0x7E, 0x80, 0x84, 0x88, 0x8B, 0x8F, 0x92,
0x93, 0x93, 0x91, 0x8F, 0x8C, 0x89, 0x86, 0x83, 0x80, 0x7D, 0x7B, 0x79, 0x78, 0x76, 0x74, 0x74,
0x75, 0x77, 0x79, 0x79, 0x79, 0x7A, 0x7B, 0x7E, 0x80, 0x82, 0x83, 0x84, 0x86, 0x88, 0x89, 0x8A,
0x8A, 0x8A, 0x8A, 0x8B, 0x8B, 0x8B, 0x8B, 0x8A, 0x88, 0x86, 0x83, 0x82, 0x81, 0x82, 0x82, 0x81,
0x80, 0x7F, 0x7E, 0x7D, 0x7C, 0x7B, 0x7B, 0x7C, 0x7C, 0x7C, 0x7B, 0x7B, 0x7A, 0x78, 0x76, 0x74,
0x73, 0x72, 0x72, 0x72, 0x72, 0x72, 0x73, 0x74, 0x76, 0x79, 0x7B, 0x7D, 0x7F, 0x81, 0x82, 0x85,
0x87, 0x89, 0x8A, 0x8B, 0x8D, 0x8F, 0x91, 0x91, 0x91, 0x90, 0x8E, 0x8C, 0x8A, 0x88, 0x86, 0x84,
0x83, 0x81, 0x80, 0x7E, 0x7D, 0x7D, 0x7D, 0x7D, 0x7C, 0x7B, 0x7B, 0x7B, 0x7B, 0x7A, 0x7A, 0x7A,
0x7B, 0x7E, 0x81, 0x83, 0x84, 0x84, 0x83, 0x82, 0x80, 0x7D, 0x7B, 0x7A, 0x79, 0x79, 0x79, 0x7A,
0x7A, 0x7B, 0x7C, 0x7E, 0x81, 0x83, 0x85, 0x86, 0x87, 0x87, 0x87, 0x85, 0x83, 0x81, 0x7F, 0x7C,
0x7A, 0x77, 0x75, 0x74, 0x74, 0x74, 0x76, 0x78, 0x7B, 0x7E, 0x81, 0x83, 0x85, 0x86, 0x88, 0x89,
0x89, 0x88, 0x87, 0x87, 0x86, 0x87, 0x87, 0x87, 0x87, 0x87, 0x86, 0x85, 0x84, 0x82, 0x80, 0x7E,
0x7C, 0x7B, 0x79, 0x78, 0x76, 0x76, 0x76, 0x76, 0x74, 0x73, 0x73, 0x74, 0x75, 0x76, 0x77, 0x77,
0x78, 0x7A, 0x7C, 0x7E, 0x7F, 0x81, 0x83, 0x84, 0x85, 0x86, 0x87, 0x87, 0x88, 0x89, 0x8A, 0x8A,
0x8A, 0x89, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x86, 0x86, 0x85, 0x83, 0x81, 0x7F, 0x7C, 0x7A,
0x77, 0x75, 0x73, 0x72, 0x72, 0x73, 0x73, 0x73, 0x73, 0x72, 0x72, 0x73, 0x75, 0x77, 0x7B, 0x7E,
0x81, 0x83, 0x85, 0x87, 0x8A, 0x8B, 0x8D, 0x8D, 0x8D, 0x8C, 0x8A, 0x89, 0x87, 0x85, 0x84, 0x83,
0x82, 0x82, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83, 0x82, 0x81, 0x80, 0x7F, 0x7E, 0x7E, 0x7D, 0x7D,
0x7C, 0x7C, 0x7B, 0x7A, 0x79, 0x79, 0x79, 0x7A, 0x7C, 0x7E, 0x80, 0x81, 0x82, 0x82, 0x81, 0x80,
0x7E, 0x7E, 0x7D, 0x7D, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x80, 0x80, 0x80,
0x7F, 0x7E, 0x7D, 0x7C, 0x7B, 0x7A, 0x79, 0x78, 0x78, 0x78, 0x79, 0x7A, 0x7B, 0x7D, 0x7F, 0x81,
0x84, 0x87, 0x8A, 0x8B, 0x8C, 0x8D, 0x8D, 0x8C, 0x8B, 0x8A, 0x8A, 0x89, 0x87, 0x85, 0x83, 0x81,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7D, 0x7B, 0x79, 0x77, 0x76, 0x75, 0x75, 0x76,
0x77, 0x77, 0x78, 0x79, 0x7B, 0x7D, 0x80, 0x82, 0x83, 0x84, 0x83, 0x82, 0x81, 0x80, 0x80, 0x80,
0x81, 0x81, 0x82, 0x83, 0x83, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x83, 0x82, 0x81,
0x81, 0x80, 0x80, 0x7F, 0x7D, 0x7C, 0x79, 0x77, 0x76, 0x75, 0x75, 0x75, 0x76, 0x76, 0x77, 0x79,
0x7B, 0x7D, 0x80, 0x83, 0x86, 0x88, 0x8A, 0x8B, 0x8B, 0x8B, 0x8A, 0x89, 0x88, 0x87, 0x86, 0x85,
0x84, 0x82, 0x80, 0x7E, 0x7D, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, 0x7C, 0x7B, 0x7B, 0x7A, 0x79, 0x78,
0x78, 0x77, 0x77, 0x78, 0x78, 0x79, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x7C, 0x7E, 0x80, 0x82, 0x82,
0x82, 0x82, 0x81, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
0x88, 0x88, 0x87, 0x84, 0x82, 0x7F, 0x7D, 0x7B, 0x7A, 0x7A, 0x7A, 0x7B, 0x7C, 0x7C, 0x7D, 0x7E,
0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x88, 0x87, 0x87, 0x86, 0x85, 0x84,
0x83, 0x81, 0x80, 0x7E, 0x7D, 0x7B, 0x79, 0x78, 0x76, 0x76, 0x76, 0x77, 0x78, 0x78, 0x79, 0x79,
0x7A, 0x7A, 0x79, 0x79, 0x78, 0x78, 0x77, 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x7B, 0x7D, 0x7F,
0x81, 0x82, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86, 0x87, 0x87, 0x88, 0x87, 0x87, 0x86, 0x86,
0x85, 0x84, 0x83, 0x82, 0x81, 0x7F, 0x7D, 0x7C, 0x7B, 0x7A, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x7B,
0x7B, 0x7C, 0x7D, 0x7F, 0x81, 0x83, 0x85, 0x86, 0x86, 0x87, 0x88, 0x8A, 0x8C, 0x8E, 0x8F, 0x8F,
0x8F, 0x8D, 0x8C, 0x8A, 0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x80, 0x7E, 0x7C, 0x7A, 0x79,
0x78, 0x77, 0x76, 0x75, 0x73, 0x72, 0x71, 0x70, 0x6F, 0x70, 0x71, 0x73, 0x75, 0x77, 0x79, 0x7B,
0x7C, 0x7D, 0x7D, 0x7D, 0x7D, 0x7E, 0x80, 0x82, 0x83, 0x85, 0x86, 0x86, 0x86, 0x87, 0x87, 0x87,
0x88, 0x88, 0x88, 0x87, 0x87, 0x87, 0x87, 0x86, 0x85, 0x83, 0x82, 0x80, 0x7F, 0x7D, 0x7C, 0x7B,
0x7A, 0x7A, 0x7A, 0x7B, 0x7C, 0x7E, 0x7F, 0x80, 0x81, 0x83, 0x84, 0x86, 0x88, 0x89, 0x8B, 0x8B,
0x8C, 0x8C, 0x8C, 0x8B, 0x8A, 0x88, 0x86, 0x83, 0x81, 0x7E, 0x7D, 0x7B, 0x7A, 0x78, 0x77, 0x76,
0x75, 0x74, 0x73, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x77, 0x78,
0x79, 0x7B, 0x7D, 0x7E, 0x80, 0x82, 0x83, 0x84, 0x85, 0x85, 0x86, 0x87, 0x88, 0x89, 0x89, 0x88,
0x88, 0x87, 0x86, 0x85, 0x85, 0x85, 0x84, 0x84, 0x83, 0x83, 0x82, 0x81, 0x80, 0x7F, 0x7E, 0x7C,
0x7B, 0x79, 0x78, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80,
0x81, 0x82, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x81, 0x81, 0x80, 0x7F, 0x7F, 0x7F, 0x7F,
0x7F, 0x7F, 0x7F, 0x7E, 0x7C, 0x7B, 0x7A, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x7A, 0x7B, 0x7C,
0x7C, 0x7D, 0x7D, 0x7E, 0x7E, 0x7F, 0x80, 0x80, 0x82, 0x83, 0x84, 0x86, 0x87, 0x89, 0x8A, 0x8B,
0x8B, 0x8A, 0x8A, 0x89, 0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x82, 0x81, 0x80, 0x7F, 0x7F,
0x7F, 0x7F, 0x7F, 0x80, 0x80, 0x81, 0x80, 0x80, 0x7F, 0x7D, 0x7C, 0x7C, 0x7B, 0x7B, 0x7C, 0x7C,
0x7D, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7E, 0x7D, 0x7C,
0x7B, 0x7B, 0x7A, 0x7A, 0x79, 0x78, 0x77, 0x76, 0x76, 0x76, 0x77, 0x79, 0x7A, 0x7C, 0x7C, 0x7D,
0x7D, 0x7E, 0x7E, 0x7F, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x87, 0x88, 0x88, 0x89, 0x8A,
0x8B, 0x8C, 0x8D, 0x8D, 0x8D, 0x8D, 0x8C, 0x8B, 0x89, 0x87, 0x85, 0x83, 0x82, 0x80, 0x7F, 0x7D,
0x7C, 0x7A, 0x79, 0x78, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x78, 0x79, 0x79, 0x7A, 0x7B, 0x7B,
0x7C, 0x7C, 0x7D, 0x7D, 0x7D, 0x7C, 0x7C, 0x7D, 0x7D, 0x7E, 0x7E, 0x7F, 0x81, 0x81, 0x82, 0x82,
0x82, 0x81, 0x80, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x80, 0x80, 0x7F, 0x7E, 0x7E, 0x7D, 0x7E,
0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7D, 0x7D, 0x7D, 0x7D, 0x7E, 0x80, 0x81, 0x82, 0x82, 0x83, 0x83,
0x83, 0x84, 0x84, 0x85, 0x86, 0x87, 0x87, 0x88, 0x88, 0x88, 0x88, 0x87, 0x86, 0x85, 0x84, 0x83,
0x81, 0x80, 0x7F, 0x7E, 0x7D, 0x7D, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7B,
0x7B, 0x7B, 0x7B, 0x7A, 0x7A, 0x7B, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7E, 0x7D,
0x7D, 0x7D, 0x7D, 0x7D, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, 0x7E, 0x7F, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x82, 0x83, 0x83, 0x83, 0x84, 0x84, 0x84, 0x85, 0x85,
0x86, 0x86, 0x87, 0x87, 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x85, 0x83, 0x82, 0x80, 0x7F, 0x7E,
0x7C, 0x7B, 0x7A, 0x79, 0x79, 0x79, 0x79, 0x7A, 0x7A, 0x7B, 0x7C, 0x7C, 0x7B, 0x7A, 0x79, 0x79,
0x78, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7C, 0x7D, 0x7D, 0x7D, 0x7E, 0x7F, 0x80, 0x80, 0x81, 0x82,
0x82, 0x83, 0x83, 0x83, 0x83, 0x84, 0x84, 0x84, 0x83, 0x83, 0x81, 0x80, 0x7F, 0x7F, 0x7E, 0x7E,
0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x84, 0x85, 0x85,
0x85, 0x85, 0x85, 0x85, 0x84, 0x83, 0x83, 0x82, 0x80, 0x80, 0x7F, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F,
0x7F, 0x7F, 0x7E, 0x7E, 0x7E, 0x7E, 0x7D, 0x7D, 0x7C, 0x7B, 0x7A, 0x79, 0x78, 0x77, 0x77, 0x77,
0x78, 0x79, 0x79, 0x79, 0x79, 0x7A, 0x7A, 0x7B, 0x7C, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F,
0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x83, 0x83, 0x83, 0x84, 0x85, 0x85, 0x86, 0x87, 0x87,
0x87, 0x86, 0x86, 0x85, 0x84, 0x83, 0x83, 0x82, 0x83, 0x82, 0x82, 0x81, 0x81, 0x81, 0x81, 0x81,
0x81, 0x81, 0x82, 0x82, 0x81, 0x81, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x7E, 0x7E, 0x7D, 0x7D, 0x7D,
0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7E, 0x7D,
0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7D, 0x7E, 0x7E, 0x7F, 0x7F, 0x80,
0x80, 0x80, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
0x82, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x7F, 0x7F, 0x80, 0x81, 0x81, 0x81,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x7E, 0x7E, 0x7D,
0x7D, 0x7C, 0x7C, 0x7B, 0x7C, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82,
0x82, 0x83, 0x83, 0x83, 0x82, 0x82, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81,
0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x7F,
0x7E, 0x7E, 0x7D, 0x7D, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x80, 0x7F, 0x7F, 0x7E, 0x7E,
0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7D, 0x7D, 0x7E, 0x7E, 0x7F, 0x7E, 0x7E, 0x7E, 0x7D, 0x7D, 0x7D,
0x7D, 0x7E, 0x7E, 0x7F, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x83, 0x83, 0x82, 0x82, 0x81, 0x81,
0x81, 0x81, 0x81, 0x82, 0x82, 0x81, 0x81, 0x81, 0x80, 0x80, 0x7F, 0x7F, 0x7E, 0x7E, 0x7E, 0x7E,
0x7E, 0x7E, 0x7E, 0x7E, 0x7D, 0x7D, 0x7D, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7E, 0x7E,
0x7D, 0x7C, 0x7C, 0x7D, 0x7D, 0x7E, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81,
0x81, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
0x82, 0x82, 0x82, 0x82, 0x81, 0x81, 0x80, 0x7E, 0x7D, 0x7C, 0x7C, 0x7B, 0x7B, 0x7B, 0x7B, 0x7C,
0x7C, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D,
0x7D, 0x7D, 0x7E, 0x7E, 0x7F, 0x7F, 0x80, 0x81, 0x82, 0x82, 0x83, 0x83, 0x83, 0x84, 0x84, 0x84,
0x83, 0x82, 0x81, 0x81, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80,
0x80, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,
0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x80, 0x80, 0x81, 0x81, 0x80,
0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81,
0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x83, 0x82, 0x82, 0x82, 0x82, 0x81, 0x81,
0x80, 0x7F, 0x7E, 0x7E, 0x7D, 0x7D, 0x7C, 0x7C, 0x7B, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D,
0x7D, 0x7E, 0x7E, 0x7D, 0x7D, 0x7D, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, 0x7E, 0x7F, 0x7F, 0x80,
0x80, 0x80, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x86, 0x86, 0x85, 0x84, 0x84, 0x83, 0x82,
0x82, 0x82, 0x81, 0x81, 0x80, 0x7F, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,
0x7F, 0x7F, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x7E, 0x7E, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7E,
0x7E, 0x7F, 0x7F, 0x80, 0x80, 0x7F, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E,
0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80,
0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81,
0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, 0x7F, 0x7F, 0x7E, 0x7D, 0x7D,
0x7D, 0x7D, 0x7D, 0x7E, 0x7E, 0x7F, 0x80, 0x80, 0x80, 0x81, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81,
0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x7E, 0x7E, 0x7D, 0x7D, 0x7E, 0x7E,
0x7E, 0x7D, 0x7D, 0x7D, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7E, 0x7E, 0x7E, 0x7F,
0x7F, 0x80, 0x81, 0x81, 0x82, 0x82, 0x83, 0x83, 0x84, 0x84, 0x84, 0x85, 0x84, 0x84, 0x84, 0x83,
0x82, 0x81, 0x80, 0x80, 0x7F, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7E, 0x7F,
0x7E, 0x7E, 0x7F, 0x7F, 0x80, 0x80, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7E, 0x7D, 0x7D, 0x7D, 0x7E,
0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F,
0x7F, 0x7E, 0x7D, 0x7D, 0x7C, 0x7C, 0x7C, 0x7B, 0x7B, 0x7B, 0x7C, 0x7D, 0x7D, 0x7E, 0x7F, 0x7F,
0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82,
0x82, 0x82, 0x82, 0x82, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x7F, 0x7E, 0x7E, 0x7D, 0x7D,
0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x80, 0x7F, 0x7F, 0x7F, 0x7F,
0x7F, 0x7F, 0x7E, 0x7E, 0x7E, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7E, 0x7F, 0x7F,
0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x83, 0x83, 0x84, 0x85, 0x85, 0x86, 0x85, 0x85, 0x85,
0x84, 0x84, 0x83, 0x83, 0x82, 0x83, 0x82, 0x82, 0x82, 0x81, 0x82, 0x82, 0x82, 0x81, 0x81, 0x81,
0x81, 0x81, 0x81, 0x80, 0x7F, 0x7F, 0x7F, 0x7E, 0x7E, 0x7E, 0x7D, 0x7D, 0x7C, 0x7C, 0x7B, 0x7C,
0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7B, 0x7B, 0x7B, 0x7C, 0x7C, 0x7D, 0x7E, 0x7F, 0x7F,
0x7F, 0x7F, 0x80, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,
0x80, 0x80, 0x81, 0x81, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x81, 0x81,
0x80, 0x80, 0x80, 0x7F, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x80, 0x80, 0x81, 0x81,
0x82, 0x82, 0x82, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,
0x7F, 0x7F, 0x7F, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x80,
0x80, 0x80, 0x80, 0x81, 0x81, 0x82, 0x82, 0x82, 0x83, 0x83, 0x84, 0x83, 0x83, 0x82, 0x82, 0x82,
0x81, 0x80, 0x80, 0x7F, 0x7F, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7D, 0x7D,
0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7E, 0x7E, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x81, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
0x81, 0x81, 0x81, 0x80, 0x80, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F,
0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x7F, 0x7F, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x80, 0x80, 0x7F, 0x7E, 0x7E,
0x7E, 0x7E, 0x7E, 0x7D, 0x7D, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E,
0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F,
0x7E, 0x7E, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
0x82, 0x82, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x7F,
0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x80, 0x80, 0x81, 0x81, 0x81, 0x80, 0x80, 0x7F, 0x7F, 0x7E,
0x7D, 0x7D, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, 0x7E, 0x7F, 0x7F, 0x80, 0x80, 0x81, 0x81, 0x81,
0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
0x81, 0x81, 0x80, 0x80, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,
0x7F, 0x7F, 0x7E, 0x7E, 0x7E, 0x7E, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7E, 0x7F, 0x7F, 0x7F,
0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81,
0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82,
0x81, 0x81, 0x81, 0x80, 0x7F, 0x7F, 0x7F, 0x7E, 0x7E, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7E,
0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,
0x7F, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x7F, 0x7E, 0x7E, 0x7E,
0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80,
0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F,
0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
0x80, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80,
0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x80, 0x7F, 0x80, 0x7F, 0x80
};

49
startup.h Normal file
View File

@ -0,0 +1,49 @@
// startup.h
#ifndef STARTUP_H
#define STARTUP_H
/* Found in the *start.S file, implemented in assembler */
extern void _start( void );
extern void _enable_interrupts( void );
extern int _disable_interrupts( void );
extern unsigned int _get_cpsr();
extern unsigned int _get_stack_pointer();
extern void _enable_unaligned_access();
extern void _enable_l1_cache();
extern void _invalidate_icache();
extern void _invalidate_dcache();
extern void _clean_invalidate_dcache();
extern void _invalidate_dcache_mva(void *address);
extern void _clean_invalidate_dcache_mva(void *address);
extern void _invalidate_dtlb();
extern void _invalidate_dtlb_mva(void *address);
extern void _data_memory_barrier();
extern unsigned int _get_core();
extern void _init_core();
extern void _spin_core();
#ifdef HAS_40PINS
extern void _toggle_test_pin(int count);
#endif
#endif

7187
stb_image.h Normal file

File diff suppressed because it is too large Load Diff

21
stb_image_config.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef STB_IMAGE_CONFIG_H
#define STB_IMAGE_CONFIG_H
#define STBI_NO_JPEG
#define STBI_NO_BMP
#define STBI_NO_PSD
#define STBI_NO_TGA
#define STBI_NO_HDR
#define STBI_NO_PIC
#define STBI_NO_PNM
#define STBI_NO_LINEAR
#define STBI_NO_HDR
#define STBI_NO_STDIO
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#endif

13
types.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef types_h
#define types_h
#include <stddef.h>
#include <uspi\types.h>
#include "integer.h"
typedef unsigned long long u64;
typedef signed long long s64;
#endif

216
uspi/include/uspi.h Normal file
View File

@ -0,0 +1,216 @@
//
// uspi.h
//
// Services provided by the USPi library
//
// USPi - An USB driver for Raspberry Pi written in C
// Copyright (C) 2014 R. Stange <rsta2@o2online.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _uspi_h
#define _uspi_h
#ifdef __cplusplus
extern "C" {
#endif
//
// USPi initialization
//
// returns 0 on failure
int USPiInitialize (void);
//
// Keyboard device
//
struct TUSBKeyboardDevice;
// returns != 0 if available
int USPiKeyboardAvailable (void);
// "cooked mode"
typedef void TUSPiKeyPressedHandler (const char *pString);
void USPiKeyboardRegisterKeyPressedHandler (TUSPiKeyPressedHandler *pKeyPressedHandler);
// This handler is called when Ctrl-Alt-Del is pressed.
typedef void TUSPiShutdownHandler (void);
void USPiKeyboardRegisterShutdownHandler (TUSPiShutdownHandler *pShutdownHandler);
// "raw mode" (if this handler is registered the others are ignored)
// The raw handler is called when the keyboard sends a status report (on status change and/or continously).
typedef void TUSPiKeyStatusHandlerRaw (struct TUSBKeyboardDevice* device, unsigned char ucModifiers,
const unsigned char RawKeys[6]); // key code or 0 in each byte
void USPiKeyboardRegisterKeyStatusHandlerRaw (TUSPiKeyStatusHandlerRaw *pKeyStatusHandlerRaw);
// ucModifiers (bit is set if modifier key is pressed)
#define LCTRL (1 << 0)
#define LSHIFT (1 << 1)
#define ALT (1 << 2)
#define LWIN (1 << 3)
#define RCTRL (1 << 4)
#define RSHIFT (1 << 5)
#define ALTGR (1 << 6)
#define RWIN (1 << 7)
//
// Mouse device
//
// returns != 0 if available
int USPiMouseAvailable (void);
// The status handler is called when the mouse sends a status report.
typedef void TUSPiMouseStatusHandler (unsigned nButtons,
int nDisplacementX, // -127..127
int nDisplacementY); // -127..127
void USPiMouseRegisterStatusHandler (TUSPiMouseStatusHandler *pStatusHandler);
// nButtons (bit is set if button is pressed)
#define MOUSE_BUTTON1 (1 << 0)
#define MOUSE_BUTTON2 (1 << 1)
#define MOUSE_BUTTON3 (1 << 2)
// ucModifiers (bit is set if modifier key is pressed)
#define LCTRL (1 << 0)
#define LSHIFT (1 << 1)
#define ALT (1 << 2)
#define LWIN (1 << 3)
#define RCTRL (1 << 4)
#define RSHIFT (1 << 5)
#define ALTGR (1 << 6)
#define RWIN (1 << 7)
//
// Mass storage device
//
// returns number of available devices
int USPiMassStorageDeviceAvailable (void);
#define USPI_BLOCK_SIZE 512 // other block sizes are not supported
// ullOffset and nCount must be multiple of USPI_BLOCK_SIZE
// returns number of read bytes or < 0 on failure
// nDeviceIndex is 0-based
int USPiMassStorageDeviceRead (unsigned long long ullOffset, void *pBuffer, unsigned nCount, unsigned nDeviceIndex);
// ullOffset and nCount must be multiple of USPI_BLOCK_SIZE
// returns number of written bytes or < 0 on failure
// nDeviceIndex is 0-based
int USPiMassStorageDeviceWrite (unsigned long long ullOffset, const void *pBuffer, unsigned nCount, unsigned nDeviceIndex);
// returns the number of available blocks of USPI_BLOCK_SIZE or 0 on failure
unsigned USPiMassStorageDeviceGetCapacity (unsigned nDeviceIndex);
//
// Ethernet services
//
// (You should delay 2 seconds after USPiInitialize before accessing the Ethernet.)
//
// checks the controller only, not if Ethernet link is up
// returns != 0 if available
int USPiEthernetAvailable (void);
void USPiGetMACAddress (unsigned char Buffer[6]);
// returns 0 on failure
int USPiSendFrame (const void *pBuffer, unsigned nLength);
// pBuffer must have size USPI_FRAME_BUFFER_SIZE
// returns 0 if no frame is available or on failure
#define USPI_FRAME_BUFFER_SIZE 1600
int USPiReceiveFrame (void *pBuffer, unsigned *pResultLength);
//
// GamePad device
//
// returns number of available devices
int USPiGamePadAvailable (void);
#define MAX_AXIS 6
#define MAX_HATS 6
typedef struct USPiGamePadState
{
int naxes;
struct
{
int value;
int minimum;
int maximum;
} axes[MAX_AXIS];
int nhats;
int hats[MAX_HATS];
int nbuttons;
unsigned int buttons;
}
USPiGamePadState;
// returns 0 on failure
const USPiGamePadState *USPiGamePadGetStatus (unsigned nDeviceIndex); // nDeviceIndex is 0-based
typedef void TGamePadStatusHandler (unsigned nDeviceIndex, const USPiGamePadState *pGamePadState);
void USPiGamePadRegisterStatusHandler (TGamePadStatusHandler *pStatusHandler);
//
// MIDI device
//
// returns != 0 if a MIDI device is available
int USPiMIDIAvailable (void);
// The packet handler is called once for each MIDI event packet received from the device.
typedef void TUSPiMIDIPacketHandler (unsigned nCable, unsigned nLength, u8 *pPacket);
void USPiMIDIRegisterPacketHandler (TUSPiMIDIPacketHandler *pPacketHandler);
//
// USB device information
//
#define KEYBOARD_CLASS 1
#define MOUSE_CLASS 2
#define STORAGE_CLASS 3
#define ETHERNET_CLASS 4
#define GAMEPAD_CLASS 5
#define MIDI_CLASS 6
typedef struct TUSPiDeviceInformation
{
// from USB device descriptor
unsigned short idVendor;
unsigned short idProduct;
unsigned short bcdDevice;
// points to a buffer in the USPi library, empty string if not available
const char *pManufacturer;
const char *pProduct;
}
TUSPiDeviceInformation;
// returns 0 on failure
int USPiDeviceGetInformation (unsigned nClass, // see above
unsigned nDeviceIndex, // 0-based index
TUSPiDeviceInformation *pInfo); // provided buffer is filled
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,39 @@
//
// assert.h
//
// USPi - An USB driver for Raspberry Pi written in C
// Copyright (C) 2014 R. Stange <rsta2@o2online.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _uspi_assert_h
#define _uspi_assert_h
#ifdef __cplusplus
extern "C" {
#endif
#ifdef NDEBUG
#define assert(expr) ((void) 0)
#else
void uspi_assertion_failed (const char *pExpr, const char *pFile, unsigned nLine);
#define assert(expr) ((expr) ? ((void) 0) : uspi_assertion_failed (#expr, __FILE__, __LINE__))
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,59 @@
//
// bcm2835.h
//
// USPi - An USB driver for Raspberry Pi written in C
// Copyright (C) 2014-2017 R. Stange <rsta2@o2online.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _uspi_bcm2835_h
#define _uspi_bcm2835_h
#include <uspios.h>
#if RASPPI == 1
#define ARM_IO_BASE 0x20000000
#else
#define ARM_IO_BASE 0x3F000000
#endif
#define GPU_IO_BASE 0x7E000000
#define GPU_CACHED_BASE 0x40000000
#define GPU_UNCACHED_BASE 0xC0000000
#if RASPPI == 1
#ifdef GPU_L2_CACHE_ENABLED
#define GPU_MEM_BASE GPU_CACHED_BASE
#else
#define GPU_MEM_BASE GPU_UNCACHED_BASE
#endif
#else
#define GPU_MEM_BASE GPU_UNCACHED_BASE
#endif
// Convert physical ARM address into bus address
// (does even work, if a bus address is provided already)
#define BUS_ADDRESS(phys) (((phys) & ~0xC0000000) | GPU_MEM_BASE)
//
// USB Host Controller
//
#define ARM_USB_BASE (ARM_IO_BASE + 0x980000)
#define ARM_USB_CORE_BASE ARM_USB_BASE
#define ARM_USB_HOST_BASE (ARM_USB_BASE + 0x400)
#define ARM_USB_POWER (ARM_USB_BASE + 0xE00)
#endif

View File

@ -0,0 +1,49 @@
//
// devicenameservice.h
//
// USPi - An USB driver for Raspberry Pi written in C
// Copyright (C) 2014 R. Stange <rsta2@o2online.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _uspi_devicenameservice_h
#define _uspi_devicenameservice_h
#include <uspi/types.h>
typedef struct TDeviceInfo
{
struct TDeviceInfo *pNext;
char *pName;
void *pDevice;
boolean bBlockDevice;
}
TDeviceInfo;
typedef struct TDeviceNameService
{
TDeviceInfo *m_pList;
}
TDeviceNameService;
void DeviceNameService (TDeviceNameService *pThis);
void _DeviceNameService (TDeviceNameService *pThis);
void DeviceNameServiceAddDevice (TDeviceNameService *pThis, const char *pName, void *pDevice, boolean bBlockDevice);
void *DeviceNameServiceGetDevice (TDeviceNameService *pThis, const char *pName, boolean bBlockDevice);
TDeviceNameService *DeviceNameServiceGet (void);
#endif

239
uspi/include/uspi/dwhci.h Normal file
View File

@ -0,0 +1,239 @@
//
// dwhci.h
//
// The information in this file is from the Linux HS OTG driver
// which is Copyright by Synopsys Inc.
// See the file lib/README!
//
// USPi - An USB driver for Raspberry Pi written in C
// Copyright (C) 2014 R. Stange <rsta2@o2online.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _uspi_dwhci_h
#define _uspi_dwhci_h
#include <uspi/bcm2835.h>
#define DWHCI_MAX_CHANNELS 16
#define DWHCI_DATA_FIFO_SIZE 0x1000
//
// Core Registers
//
#define DWHCI_CORE_OTG_CTRL (ARM_USB_CORE_BASE + 0x000)
#define DWHCI_CORE_OTG_CTRL_HST_SET_HNP_EN (1 << 10)
#define DWHCI_CORE_OTG_INT (ARM_USB_CORE_BASE + 0x004)
#define DWHCI_CORE_AHB_CFG (ARM_USB_CORE_BASE + 0x008)
#define DWHCI_CORE_AHB_CFG_GLOBALINT_MASK (1 << 0)
#define DWHCI_CORE_AHB_CFG_MAX_AXI_BURST__SHIFT 1 // BCM2835 only
#define DWHCI_CORE_AHB_CFG_MAX_AXI_BURST__MASK (3 << 1) // BCM2835 only
#define DWHCI_CORE_AHB_CFG_WAIT_AXI_WRITES (1 << 4) // BCM2835 only
#define DWHCI_CORE_AHB_CFG_DMAENABLE (1 << 5)
#define DWHCI_CORE_AHB_CFG_AHB_SINGLE (1 << 23)
#define DWHCI_CORE_USB_CFG (ARM_USB_CORE_BASE + 0x00C)
#define DWHCI_CORE_USB_CFG_PHYIF (1 << 3)
#define DWHCI_CORE_USB_CFG_ULPI_UTMI_SEL (1 << 4)
#define DWHCI_CORE_USB_CFG_SRP_CAPABLE (1 << 8)
#define DWHCI_CORE_USB_CFG_HNP_CAPABLE (1 << 9)
#define DWHCI_CORE_USB_CFG_ULPI_FSLS (1 << 17)
#define DWHCI_CORE_USB_CFG_ULPI_CLK_SUS_M (1 << 19)
#define DWHCI_CORE_USB_CFG_ULPI_EXT_VBUS_DRV (1 << 20)
#define DWHCI_CORE_USB_CFG_TERM_SEL_DL_PULSE (1 << 22)
#define DWHCI_CORE_RESET (ARM_USB_CORE_BASE + 0x010)
#define DWHCI_CORE_RESET_SOFT_RESET (1 << 0)
#define DWHCI_CORE_RESET_RX_FIFO_FLUSH (1 << 4)
#define DWHCI_CORE_RESET_TX_FIFO_FLUSH (1 << 5)
#define DWHCI_CORE_RESET_TX_FIFO_NUM__SHIFT 6
#define DWHCI_CORE_RESET_TX_FIFO_NUM__MASK (0x1F << 6)
#define DWHCI_CORE_RESET_AHB_IDLE (1 << 31)
#define DWHCI_CORE_INT_STAT (ARM_USB_CORE_BASE + 0x014)
#define DWHCI_CORE_INT_STAT_SOF_INTR (1 << 3)
#define DWHCI_CORE_INT_STAT_PORT_INTR (1 << 24)
#define DWHCI_CORE_INT_STAT_HC_INTR (1 << 25)
#define DWHCI_CORE_INT_MASK (ARM_USB_CORE_BASE + 0x018)
#define DWHCI_CORE_INT_MASK_MODE_MISMATCH (1 << 1)
#define DWHCI_CORE_INT_MASK_SOF_INTR (1 << 3)
#define DWHCI_CORE_INT_MASK_RX_STS_Q_LVL (1 << 4)
#define DWHCI_CORE_INT_MASK_USB_SUSPEND (1 << 11)
#define DWHCI_CORE_INT_MASK_PORT_INTR (1 << 24)
#define DWHCI_CORE_INT_MASK_HC_INTR (1 << 25)
#define DWHCI_CORE_INT_MASK_CON_ID_STS_CHNG (1 << 28)
#define DWHCI_CORE_INT_MASK_DISCONNECT (1 << 29)
#define DWHCI_CORE_INT_MASK_SESS_REQ_INTR (1 << 30)
#define DWHCI_CORE_INT_MASK_WKUP_INTR (1 << 31)
#define DWHCI_CORE_RX_STAT_RD (ARM_USB_CORE_BASE + 0x01C) // RO, slave mode only
#define DWHCI_CORE_RX_STAT_POP (ARM_USB_CORE_BASE + 0x020) // RO, slave mode only
// for read and pop register in host mode
#define DWHCI_CORE_RX_STAT_CHAN_NUMBER__MASK 0xF
#define DWHCI_CORE_RX_STAT_BYTE_COUNT__SHIFT 4
#define DWHCI_CORE_RX_STAT_BYTE_COUNT__MASK (0x7FF << 4)
#define DWHCI_CORE_RX_STAT_PACKET_STATUS__SHIFT 17
#define DWHCI_CORE_RX_STAT_PACKET_STATUS__MASK (0xF << 17)
#define DWHCI_CORE_RX_STAT_PACKET_STATUS_IN 2
#define DWHCI_CORE_RX_STAT_PACKET_STATUS_IN_XFER_COMP 3
#define DWHCI_CORE_RX_STAT_PACKET_STATUS_DATA_TOGGLE_ERR 5
#define DWHCI_CORE_RX_STAT_PACKET_STATUS_CHAN_HALTED 7
#define DWHCI_CORE_RX_FIFO_SIZ (ARM_USB_CORE_BASE + 0x024)
#define DWHCI_CORE_NPER_TX_FIFO_SIZ (ARM_USB_CORE_BASE + 0x028)
#define DWHCI_CORE_NPER_TX_STAT (ARM_USB_CORE_BASE + 0x02C) // RO
#define DWHCI_CORE_NPER_TX_STAT_QUEUE_SPACE_AVL(reg) (((reg) >> 16) & 0xFF)
#define DWHCI_CORE_I2C_CTRL (ARM_USB_CORE_BASE + 0x030)
#define DWHCI_CORE_PHY_VENDOR_CTRL (ARM_USB_CORE_BASE + 0x034)
#define DWHCI_CORE_GPIO (ARM_USB_CORE_BASE + 0x038)
#define DWHCI_CORE_USER_ID (ARM_USB_CORE_BASE + 0x03C)
#define DWHCI_CORE_VENDOR_ID (ARM_USB_CORE_BASE + 0x040)
#define DWHCI_CORE_HW_CFG1 (ARM_USB_CORE_BASE + 0x044) // RO
#define DWHCI_CORE_HW_CFG2 (ARM_USB_CORE_BASE + 0x048) // RO
#define DWHCI_CORE_HW_CFG2_OP_MODE(reg) (((reg) >> 0) & 7)
#define DWHCI_CORE_HW_CFG2_ARCHITECTURE(reg) (((reg) >> 3) & 3)
#define DWHCI_CORE_HW_CFG2_HS_PHY_TYPE(reg) (((reg) >> 6) & 3)
#define DWHCI_CORE_HW_CFG2_HS_PHY_TYPE_NOT_SUPPORTED 0
#define DWHCI_CORE_HW_CFG2_HS_PHY_TYPE_UTMI 1
#define DWHCI_CORE_HW_CFG2_HS_PHY_TYPE_ULPI 2
#define DWHCI_CORE_HW_CFG2_HS_PHY_TYPE_UTMI_ULPI 3
#define DWHCI_CORE_HW_CFG2_FS_PHY_TYPE(reg) (((reg) >> 8) & 3)
#define DWHCI_CORE_HW_CFG2_FS_PHY_TYPE_DEDICATED 1
#define DWHCI_CORE_HW_CFG2_NUM_HOST_CHANNELS(reg) ((((reg) >> 14) & 0xF) + 1)
#define DWHCI_CORE_HW_CFG3 (ARM_USB_CORE_BASE + 0x04C) // RO
#define DWHCI_CORE_HW_CFG3_DFIFO_DEPTH(reg) (((reg) >> 16) & 0xFFFF)
#define DWHCI_CORE_HW_CFG4 (ARM_USB_CORE_BASE + 0x050) // RO
#define DWHCI_CORE_HW_CFG4_DED_FIFO_EN (1 << 25)
#define DWHCI_CORE_HW_CFG4_NUM_IN_EPS(reg) (((reg) >> 26) & 0xF)
#define DWHCI_CORE_LPM_CFG (ARM_USB_CORE_BASE + 0x054)
#define DWHCI_CORE_POWER_DOWN (ARM_USB_CORE_BASE + 0x058)
#define DWHCI_CORE_DFIFO_CFG (ARM_USB_CORE_BASE + 0x05C)
#define DWHCI_CORE_DFIFO_CFG_EPINFO_BASE__SHIFT 16
#define DWHCI_CORE_DFIFO_CFG_EPINFO_BASE__MASK (0xFFFF << 16)
#define DWHCI_CORE_ADP_CTRL (ARM_USB_CORE_BASE + 0x060)
// gap
#define DWHCI_VENDOR_MDIO_CTRL (ARM_USB_CORE_BASE + 0x080) // BCM2835 only
#define DWHCI_VENDOR_MDIO_DATA (ARM_USB_CORE_BASE + 0x084) // BCM2835 only
#define DWHCI_VENDOR_VBUS_DRV (ARM_USB_CORE_BASE + 0x088) // BCM2835 only
// gap
#define DWHCI_CORE_HOST_PER_TX_FIFO_SIZ (ARM_USB_CORE_BASE + 0x100)
// fifo := 0..14 :
#define DWHCI_CORE_DEV_PER_TX_FIFO(fifo) (ARM_USB_CORE_BASE + 0x104 + (fifo)*4) // dedicated FIFOs on
#define DWHCI_CORE_DEV_TX_FIFO(fifo) (ARM_USB_CORE_BASE + 0x104 + (fifo)*4) // dedicated FIFOs off
//
// Host Registers
//
#define DWHCI_HOST_CFG (ARM_USB_HOST_BASE + 0x000)
#define DWHCI_HOST_CFG_FSLS_PCLK_SEL__SHIFT 0
#define DWHCI_HOST_CFG_FSLS_PCLK_SEL__MASK (3 << 0)
#define DWHCI_HOST_CFG_FSLS_PCLK_SEL_30_60_MHZ 0
#define DWHCI_HOST_CFG_FSLS_PCLK_SEL_48_MHZ 1
#define DWHCI_HOST_CFG_FSLS_PCLK_SEL_6_MHZ 2
#define DWHCI_HOST_FRM_INTERVAL (ARM_USB_HOST_BASE + 0x004)
#define DWHCI_HOST_FRM_NUM (ARM_USB_HOST_BASE + 0x008)
#define DWHCI_HOST_FRM_NUM_NUMBER(reg) ((reg) & 0xFFFF)
#define DWHCI_MAX_FRAME_NUMBER 0x3FFF
#define DWHCI_HOST_FRM_NUM_REMAINING(reg) (((reg) >> 16) & 0xFFFF)
// gap
#define DWHCI_HOST_PER_TX_FIFO_STAT (ARM_USB_HOST_BASE + 0x010)
#define DWHCI_HOST_ALLCHAN_INT (ARM_USB_HOST_BASE + 0x014)
#define DWHCI_HOST_ALLCHAN_INT_MASK (ARM_USB_HOST_BASE + 0x018)
#define DWHCI_HOST_FRMLST_BASE (ARM_USB_HOST_BASE + 0x01C)
// gap
#define DWHCI_HOST_PORT (ARM_USB_HOST_BASE + 0x040)
#define DWHCI_HOST_PORT_CONNECT (1 << 0)
#define DWHCI_HOST_PORT_CONNECT_CHANGED (1 << 1)
#define DWHCI_HOST_PORT_ENABLE (1 << 2)
#define DWHCI_HOST_PORT_ENABLE_CHANGED (1 << 3)
#define DWHCI_HOST_PORT_OVERCURRENT (1 << 4)
#define DWHCI_HOST_PORT_OVERCURRENT_CHANGED (1 << 5)
#define DWHCI_HOST_PORT_RESET (1 << 8)
#define DWHCI_HOST_PORT_POWER (1 << 12)
#define DWHCI_HOST_PORT_SPEED(reg) (((reg) >> 17) & 3)
#define DWHCI_HOST_PORT_SPEED_HIGH 0
#define DWHCI_HOST_PORT_SPEED_FULL 1
#define DWHCI_HOST_PORT_SPEED_LOW 2
#define DWHCI_HOST_PORT_DEFAULT_MASK ( DWHCI_HOST_PORT_CONNECT_CHANGED \
| DWHCI_HOST_PORT_ENABLE \
| DWHCI_HOST_PORT_ENABLE_CHANGED \
| DWHCI_HOST_PORT_OVERCURRENT_CHANGED)
// gap
// chan := 0..15 :
#define DWHCI_HOST_CHAN_CHARACTER(chan) (ARM_USB_HOST_BASE + 0x100 + (chan)*0x20)
#define DWHCI_HOST_CHAN_CHARACTER_MAX_PKT_SIZ__MASK 0x7FF
#define DWHCI_HOST_CHAN_CHARACTER_EP_NUMBER__SHIFT 11
#define DWHCI_HOST_CHAN_CHARACTER_EP_NUMBER__MASK (0xF << 11)
#define DWHCI_HOST_CHAN_CHARACTER_EP_DIRECTION_IN (1 << 15)
#define DWHCI_HOST_CHAN_CHARACTER_LOW_SPEED_DEVICE (1 << 17)
#define DWHCI_HOST_CHAN_CHARACTER_EP_TYPE__SHIFT 18
#define DWHCI_HOST_CHAN_CHARACTER_EP_TYPE__MASK (3 << 18)
#define DWHCI_HOST_CHAN_CHARACTER_EP_TYPE_CONTROL 0
#define DWHCI_HOST_CHAN_CHARACTER_EP_TYPE_ISO 1
#define DWHCI_HOST_CHAN_CHARACTER_EP_TYPE_BULK 2
#define DWHCI_HOST_CHAN_CHARACTER_EP_TYPE_INTERRUPT 3
#define DWHCI_HOST_CHAN_CHARACTER_MULTI_CNT__SHIFT 20
#define DWHCI_HOST_CHAN_CHARACTER_MULTI_CNT__MASK (3 << 20)
#define DWHCI_HOST_CHAN_CHARACTER_DEVICE_ADDRESS__SHIFT 22
#define DWHCI_HOST_CHAN_CHARACTER_DEVICE_ADDRESS__MASK (0x7F << 22)
#define DWHCI_HOST_CHAN_CHARACTER_PER_ODD_FRAME (1 << 29)
#define DWHCI_HOST_CHAN_CHARACTER_DISABLE (1 << 30)
#define DWHCI_HOST_CHAN_CHARACTER_ENABLE (1 << 31)
#define DWHCI_HOST_CHAN_SPLIT_CTRL(chan) (ARM_USB_HOST_BASE + 0x104 + (chan)*0x20)
#define DWHCI_HOST_CHAN_SPLIT_CTRL_PORT_ADDRESS__MASK 0x7F
#define DWHCI_HOST_CHAN_SPLIT_CTRL_HUB_ADDRESS__SHIFT 7
#define DWHCI_HOST_CHAN_SPLIT_CTRL_HUB_ADDRESS__MASK (0x7F << 7)
#define DWHCI_HOST_CHAN_SPLIT_CTRL_XACT_POS__SHIFT 14
#define DWHCI_HOST_CHAN_SPLIT_CTRL_XACT_POS__MASK (3 << 14)
#define DWHCI_HOST_CHAN_SPLIT_CTRL_ALL 3
#define DWHCI_HOST_CHAN_SPLIT_CTRL_COMPLETE_SPLIT (1 << 16)
#define DWHCI_HOST_CHAN_SPLIT_CTRL_SPLIT_ENABLE (1 << 31)
#define DWHCI_HOST_CHAN_INT(chan) (ARM_USB_HOST_BASE + 0x108 + (chan)*0x20)
#define DWHCI_HOST_CHAN_INT_XFER_COMPLETE (1 << 0)
#define DWHCI_HOST_CHAN_INT_HALTED (1 << 1)
#define DWHCI_HOST_CHAN_INT_AHB_ERROR (1 << 2)
#define DWHCI_HOST_CHAN_INT_STALL (1 << 3)
#define DWHCI_HOST_CHAN_INT_NAK (1 << 4)
#define DWHCI_HOST_CHAN_INT_ACK (1 << 5)
#define DWHCI_HOST_CHAN_INT_NYET (1 << 6)
#define DWHCI_HOST_CHAN_INT_XACT_ERROR (1 << 7)
#define DWHCI_HOST_CHAN_INT_BABBLE_ERROR (1 << 8)
#define DWHCI_HOST_CHAN_INT_FRAME_OVERRUN (1 << 9)
#define DWHCI_HOST_CHAN_INT_DATA_TOGGLE_ERROR (1 << 10)
#define DWHCI_HOST_CHAN_INT_ERROR_MASK ( DWHCI_HOST_CHAN_INT_AHB_ERROR \
| DWHCI_HOST_CHAN_INT_STALL \
| DWHCI_HOST_CHAN_INT_XACT_ERROR \
| DWHCI_HOST_CHAN_INT_BABBLE_ERROR \
| DWHCI_HOST_CHAN_INT_FRAME_OVERRUN \
| DWHCI_HOST_CHAN_INT_DATA_TOGGLE_ERROR)
#define DWHCI_HOST_CHAN_INT_MASK(chan) (ARM_USB_HOST_BASE + 0x10C + (chan)*0x20)
#define DWHCI_HOST_CHAN_XFER_SIZ(chan) (ARM_USB_HOST_BASE + 0x110 + (chan)*0x20)
#define DWHCI_HOST_CHAN_XFER_SIZ_BYTES__MASK 0x7FFFF
#define DWHCI_HOST_CHAN_XFER_SIZ_PACKETS__SHIFT 19
#define DWHCI_HOST_CHAN_XFER_SIZ_PACKETS__MASK (0x3FF << 19)
#define DWHCI_HOST_CHAN_XFER_SIZ_PACKETS(reg) (((reg) >> 19) & 0x3FF)
#define DWHCI_HOST_CHAN_XFER_SIZ_PID__SHIFT 29
#define DWHCI_HOST_CHAN_XFER_SIZ_PID__MASK (3 << 29)
#define DWHCI_HOST_CHAN_XFER_SIZ_PID(reg) (((reg) >> 29) & 3)
#define DWHCI_HOST_CHAN_XFER_SIZ_PID_DATA0 0
#define DWHCI_HOST_CHAN_XFER_SIZ_PID_DATA1 2
#define DWHCI_HOST_CHAN_XFER_SIZ_PID_DATA2 1
#define DWHCI_HOST_CHAN_XFER_SIZ_PID_MDATA 3 // non-control transfer
#define DWHCI_HOST_CHAN_XFER_SIZ_PID_SETUP 3
#define DWHCI_HOST_CHAN_DMA_ADDR(chan) (ARM_USB_HOST_BASE + 0x114 + (chan)*0x20)
// gap
#define DWHCI_HOST_CHAN_DMA_BUF(chan) (ARM_USB_HOST_BASE + 0x11C + (chan)*0x20) // DDMA only
//
// Data FIFOs (non-DMA mode only)
//
#define DWHCI_DATA_FIFO(chan) (ARM_USB_HOST_BASE + 0x1000 + (chan)*DWHCI_DATA_FIFO_SIZE)
#endif

View File

@ -0,0 +1,85 @@
//
// dwhcidevice.h
//
// USPi - An USB driver for Raspberry Pi written in C
// Copyright (C) 2014 R. Stange <rsta2@o2online.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _dwhcidevice_h
#define _dwhcidevice_h
#include <uspi/usb.h>
#include <uspi/usbendpoint.h>
#include <uspi/usbrequest.h>
#include <uspi/dwhcirootport.h>
#include <uspi/dwhcixferstagedata.h>
#include <uspi/dwhciregister.h>
#include <uspi/dwhci.h>
#include <uspi/usb.h>
#include <uspi/types.h>
#include <uspios.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct TDWHCIDevice
{
unsigned m_nChannels;
volatile unsigned m_nChannelAllocated; // one bit per channel, set if allocated
TDWHCITransferStageData *m_pStageData[DWHCI_MAX_CHANNELS];
volatile boolean m_bWaiting;
TDWHCIRootPort m_RootPort;
}
TDWHCIDevice;
void DWHCIDevice (TDWHCIDevice *pThis);
void _DWHCIDevice (TDWHCIDevice *pThis);
boolean DWHCIDeviceInitialize (TDWHCIDevice *pThis);
// returns resulting length or < 0 on failure
int DWHCIDeviceGetDescriptor (TDWHCIDevice *pThis, TUSBEndpoint *pEndpoint,
unsigned char ucType, unsigned char ucIndex,
void *pBuffer, unsigned nBufSize,
unsigned char ucRequestType /* = REQUEST_IN */);
boolean DWHCIDeviceSetAddress (TDWHCIDevice *pThis, TUSBEndpoint *pEndpoint, u8 ucDeviceAddress);
boolean DWHCIDeviceSetConfiguration (TDWHCIDevice *pThis, TUSBEndpoint *pEndpoint, u8 ucConfigurationValue);
// returns resulting length or < 0 on failure
int DWHCIDeviceControlMessage (TDWHCIDevice *pThis, TUSBEndpoint *pEndpoint,
u8 ucRequestType, u8 ucRequest, u16 usValue, u16 usIndex,
void *pData, u16 usDataSize);
// returns resulting length or < 0 on failure
int DWHCIDeviceTransfer (TDWHCIDevice *pThis, TUSBEndpoint *pEndpoint, void *pBuffer, unsigned nBufSize);
boolean DWHCIDeviceSubmitBlockingRequest (TDWHCIDevice *pThis, TUSBRequest *pURB);
boolean DWHCIDeviceSubmitAsyncRequest (TDWHCIDevice *pThis, TUSBRequest *pURB);
TUSBSpeed DWHCIDeviceGetPortSpeed (TDWHCIDevice *pThis);
boolean DWHCIDeviceOvercurrentDetected (TDWHCIDevice *pThis);
void DWHCIDeviceDisableRootPort (TDWHCIDevice *pThis);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,46 @@
//
// dwhciframeschednper.h
//
// USPi - An USB driver for Raspberry Pi written in C
// Copyright (C) 2014 R. Stange <rsta2@o2online.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _uspi_dwhciframeschednper_h
#define _uspi_dwhciframeschednper_h
#include <uspi/dwhciframescheduler.h>
#include <uspi/types.h>
typedef struct TDWHCIFrameSchedulerNonPeriodic
{
TDWHCIFrameScheduler m_DWHCIFrameScheduler;
unsigned m_nState;
unsigned m_nTries;
}
TDWHCIFrameSchedulerNonPeriodic;
void DWHCIFrameSchedulerNonPeriodic (TDWHCIFrameSchedulerNonPeriodic *pThis);
void _DWHCIFrameSchedulerNonPeriodic (TDWHCIFrameScheduler *pBase);
void DWHCIFrameSchedulerNonPeriodicStartSplit (TDWHCIFrameScheduler *pBase);
boolean DWHCIFrameSchedulerNonPeriodicCompleteSplit (TDWHCIFrameScheduler *pBase);
void DWHCIFrameSchedulerNonPeriodicTransactionComplete (TDWHCIFrameScheduler *pBase, u32 nStatus);
void DWHCIFrameSchedulerNonPeriodicWaitForFrame (TDWHCIFrameScheduler *pBase);
boolean DWHCIFrameSchedulerNonPeriodicIsOddFrame (TDWHCIFrameScheduler *pBase);
#endif

View File

@ -0,0 +1,46 @@
//
// dwhciframeschednsplit.h
//
// USPi - An USB driver for Raspberry Pi written in C
// Copyright (C) 2014 R. Stange <rsta2@o2online.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _uspi_dwhciframeschednsplit_h
#define _uspi_dwhciframeschednsplit_h
#include <uspi/dwhciframescheduler.h>
#include <uspi/types.h>
typedef struct TDWHCIFrameSchedulerNoSplit
{
TDWHCIFrameScheduler m_DWHCIFrameScheduler;
boolean m_bIsPeriodic;
unsigned m_nNextFrame;
}
TDWHCIFrameSchedulerNoSplit;
void DWHCIFrameSchedulerNoSplit (TDWHCIFrameSchedulerNoSplit *pThis, boolean bIsPeriodic);
void _DWHCIFrameSchedulerNoSplit (TDWHCIFrameScheduler *pBase);
void DWHCIFrameSchedulerNoSplitStartSplit (TDWHCIFrameScheduler *pBase);
boolean DWHCIFrameSchedulerNoSplitCompleteSplit (TDWHCIFrameScheduler *pBase);
void DWHCIFrameSchedulerNoSplitTransactionComplete (TDWHCIFrameScheduler *pBase, u32 nStatus);
void DWHCIFrameSchedulerNoSplitWaitForFrame (TDWHCIFrameScheduler *pBase);
boolean DWHCIFrameSchedulerNoSplitIsOddFrame (TDWHCIFrameScheduler *pBase);
#endif

View File

@ -0,0 +1,48 @@
//
// dwhciframeschedper.h
//
// USPi - An USB driver for Raspberry Pi written in C
// Copyright (C) 2014 R. Stange <rsta2@o2online.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _uspi_dwhciframeschedper_h
#define _uspi_dwhciframeschedper_h
#include <uspi/dwhciframescheduler.h>
#include <uspi/types.h>
typedef struct TDWHCIFrameSchedulerPeriodic
{
TDWHCIFrameScheduler m_DWHCIFrameScheduler;
unsigned m_nState;
unsigned m_nTries;
unsigned m_nNextFrame;
}
TDWHCIFrameSchedulerPeriodic;
void DWHCIFrameSchedulerPeriodic (TDWHCIFrameSchedulerPeriodic *pThis);
void _DWHCIFrameSchedulerPeriodic (TDWHCIFrameScheduler *pBase);
void DWHCIFrameSchedulerPeriodicStartSplit (TDWHCIFrameScheduler *pBase);
boolean DWHCIFrameSchedulerPeriodicCompleteSplit (TDWHCIFrameScheduler *pBase);
void DWHCIFrameSchedulerPeriodicTransactionComplete (TDWHCIFrameScheduler *pBase, u32 nStatus);
void DWHCIFrameSchedulerPeriodicWaitForFrame (TDWHCIFrameScheduler *pBase);
boolean DWHCIFrameSchedulerPeriodicIsOddFrame (TDWHCIFrameScheduler *pBase);
#endif

View File

@ -0,0 +1,47 @@
//
// dwhciframescheduler.h
//
// USPi - An USB driver for Raspberry Pi written in C
// Copyright (C) 2014 R. Stange <rsta2@o2online.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _uspi_dwhciframescheduler_h
#define _uspi_dwhciframescheduler_h
#include <uspi/types.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct TDWHCIFrameScheduler
{
void (*_DWHCIFrameScheduler) (struct TDWHCIFrameScheduler *pThis);
void (*StartSplit) (struct TDWHCIFrameScheduler *pThis);
boolean (*CompleteSplit) (struct TDWHCIFrameScheduler *pThis);
void (*TransactionComplete) (struct TDWHCIFrameScheduler *pThis, u32 nStatus);
void (*WaitForFrame) (struct TDWHCIFrameScheduler *pThis);
boolean (*IsOddFrame) (struct TDWHCIFrameScheduler *pThis);
}
TDWHCIFrameScheduler;
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,68 @@
//
// dwhciregister.h
//
// USPi - An USB driver for Raspberry Pi written in C
// Copyright (C) 2014 R. Stange <rsta2@o2online.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _uspi_dwhciregister_h
#define _uspi_dwhciregister_h
#include <uspi/dwhci.h>
#include <uspi/types.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct TDWHCIRegister
{
boolean m_bValid;
u32 m_nAddress;
u32 m_nBuffer;
}
TDWHCIRegister;
void DWHCIRegister (TDWHCIRegister *pThis, u32 nAddress);
void DWHCIRegister2 (TDWHCIRegister *pThis, u32 nAddress, u32 nValue);
void _DWHCIRegister (TDWHCIRegister *pThis);
u32 DWHCIRegisterRead (TDWHCIRegister *pThis);
void DWHCIRegisterWrite (TDWHCIRegister *pThis);
u32 DWHCIRegisterGet (TDWHCIRegister *pThis);
void DWHCIRegisterSet (TDWHCIRegister *pThis, u32 nValue);
boolean DWHCIRegisterIsSet (TDWHCIRegister *pThis, u32 nMask);
void DWHCIRegisterAnd (TDWHCIRegister *pThis, u32 nMask);
void DWHCIRegisterOr (TDWHCIRegister *pThis, u32 nMask);
void DWHCIRegisterClearBit (TDWHCIRegister *pThis, unsigned nBit);
void DWHCIRegisterSetBit (TDWHCIRegister *pThis, unsigned nBit);
void DWHCIRegisterClearAll (TDWHCIRegister *pThis);
void DWHCIRegisterSetAll (TDWHCIRegister *pThis);
#ifndef NDEBUG
void DWHCIRegisterDump (TDWHCIRegister *pThis);
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,49 @@
//
// dwhcirootport.h
//
// USPi - An USB driver for Raspberry Pi written in C
// Copyright (C) 2014 R. Stange <rsta2@o2online.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _uspi_dwhcirootport_h
#define _uspi_dwhcirootport_h
#include <uspi/usbdevice.h>
#include <uspi/types.h>
#ifdef __cplusplus
extern "C" {
#endif
struct TDWHCIDevice;
typedef struct TDWHCIRootPort
{
struct TDWHCIDevice *m_pHost;
TUSBDevice *m_pDevice;
}
TDWHCIRootPort;
void DWHCIRootPort (TDWHCIRootPort *pThis, struct TDWHCIDevice *pHost);
void _DWHCIRootPort (TDWHCIRootPort *pThis);
boolean DWHCIRootPortInitialize (TDWHCIRootPort *pThis);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,117 @@
//
// dwhcixferstagedata.h
//
// USPi - An USB driver for Raspberry Pi written in C
// Copyright (C) 2014 R. Stange <rsta2@o2online.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _uspi__dwhcixferstagedata_h
#define _uspi_dwhcixferstagedata_h
#include <uspi/usb.h>
#include <uspi/usbrequest.h>
#include <uspi/usbdevice.h>
#include <uspi/usbendpoint.h>
#include <uspi/dwhciframescheduler.h>
#include <uspi/types.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct TDWHCITransferStageData
{
unsigned m_nChannel; // parameters
TUSBRequest *m_pURB;
boolean m_bIn;
boolean m_bStatusStage;
boolean m_bSplitTransaction;
boolean m_bSplitComplete;
TUSBDevice *m_pDevice; // cached from *pURB
TUSBEndpoint *m_pEndpoint;
TUSBSpeed m_Speed;
u32 m_nMaxPacketSize;
u32 m_nTransferSize;
unsigned m_nPackets;
u32 m_nBytesPerTransaction;
unsigned m_nPacketsPerTransaction;
u32 m_nTotalBytesTransfered;
unsigned m_nState;
unsigned m_nSubState;
u32 m_nTransactionStatus;
u32 *m_pTempBuffer;
void *m_pBufferPointer;
TDWHCIFrameScheduler *m_pFrameScheduler;
}
TDWHCITransferStageData;
void DWHCITransferStageData (TDWHCITransferStageData *pThis, unsigned nChannel, TUSBRequest *pURB, boolean bIn, boolean bStatusStage);
void _DWHCITransferStageData (TDWHCITransferStageData *pThis);
// change status
void DWHCITransferStageDataTransactionComplete (TDWHCITransferStageData *pThis, u32 nStatus, u32 nPacketsLeft, u32 nBytesLeft);
void DWHCITransferStageDataSetSplitComplete (TDWHCITransferStageData *pThis, boolean bComplete);
void DWHCITransferStageDataSetState (TDWHCITransferStageData *pThis, unsigned nState);
unsigned DWHCITransferStageDataGetState (TDWHCITransferStageData *pThis);
void DWHCITransferStageDataSetSubState (TDWHCITransferStageData *pThis, unsigned nSubState);
unsigned DWHCITransferStageDataGetSubState (TDWHCITransferStageData *pThis);
boolean DWHCITransferStageDataBeginSplitCycle (TDWHCITransferStageData *pThis);
// get transaction parameters
unsigned DWHCITransferStageDataGetChannelNumber (TDWHCITransferStageData *pThis);
u8 DWHCITransferStageDataGetDeviceAddress (TDWHCITransferStageData *pThis);
boolean DWHCITransferStageDataIsPeriodic (TDWHCITransferStageData *pThis);
u8 DWHCITransferStageDataGetEndpointType (TDWHCITransferStageData *pThis);
u8 DWHCITransferStageDataGetEndpointNumber (TDWHCITransferStageData *pThis);
u32 DWHCITransferStageDataGetMaxPacketSize (TDWHCITransferStageData *pThis);
TUSBSpeed DWHCITransferStageDataGetSpeed (TDWHCITransferStageData *pThis);
u8 DWHCITransferStageDataGetPID (TDWHCITransferStageData *pThis);
boolean DWHCITransferStageDataIsDirectionIn (TDWHCITransferStageData *pThis);
boolean DWHCITransferStageDataIsStatusStage (TDWHCITransferStageData *pThis);
u32 DWHCITransferStageDataGetDMAAddress (TDWHCITransferStageData *pThis);
u32 DWHCITransferStageDataGetBytesToTransfer (TDWHCITransferStageData *pThis);
u32 DWHCITransferStageDataGetPacketsToTransfer (TDWHCITransferStageData *pThis);
boolean DWHCITransferStageDataIsSplit (TDWHCITransferStageData *pThis);
boolean DWHCITransferStageDataIsSplitComplete (TDWHCITransferStageData *pThis);
u8 DWHCITransferStageDataGetHubAddress (TDWHCITransferStageData *pThis);
u8 DWHCITransferStageDataGetHubPortAddress (TDWHCITransferStageData *pThis);
u8 DWHCITransferStageDataGetSplitPosition (TDWHCITransferStageData *pThis);
u32 DWHCITransferStageDataGetStatusMask (TDWHCITransferStageData *pThis);
// check status after transaction
u32 DWHCITransferStageDataGetTransactionStatus (TDWHCITransferStageData *pThis);
boolean DWHCITransferStageDataIsStageComplete (TDWHCITransferStageData *pThis);
u32 DWHCITransferStageDataGetResultLen (TDWHCITransferStageData *pThis);
TUSBRequest *DWHCITransferStageDataGetURB (TDWHCITransferStageData *pThis);
TDWHCIFrameScheduler *DWHCITransferStageDataGetFrameScheduler (TDWHCITransferStageData *pThis);
#ifdef __cplusplus
}
#endif
#endif

Some files were not shown because too many files have changed in this diff Show More