---------------------------------------------------------------------------- Genesis/SNES Game Genie Hardware Notes (C) 2007 Charles MacDonald http://cgfm2.emuviews.com ---------------------------------------------------------------------------- [05/30/07] - Initial release ---------------------------------------------------------------------------- Overview ---------------------------------------------------------------------------- The Game Genie is a ROM patching device developed by Codemasters and produced by Galoob. It sits between the game cartridge and game system where its custom chip (the SGA001 ASIC) can monitor access to cartridge ROM. For up to six unique ROM addresses, data can be substituted for the original ROM contents. This is used to patch data or program code to facilitate cheating. A switch and indicator LED on the Game Genie allow codes to be disabled or enabled depending on the switch state. This can be used to disable codes which may cause problems in one part of a game but be useful in another part. Once a game has started all ASIC registers can be "locked out" so a game program cannot intentionally or accidentally alter the Game Genie functionality during gameplay. Galoob originally offered a paid subscription service to obtain new codes for games as they were released. To prevent easy modification of existing published codes which would make their service less useful, the code format is scrambled. The Game Genie software descrambles entered codes to raw address/data pairs which are directly loaded into the ASIC registers. The Genesis Game Genie software was programmed by Robert Leyland at MicroSystems Development. There are no credits listed in the SNES version. The hardware and original concept were developed by Codemasters. ---------------------------------------------------------------------------- ASIC functional description ---------------------------------------------------------------------------- The SGA001 chip has a 68000 compatible interface utilizing the following signals: A23-A1 24-bit address bus. A0 is not present. D15-D0 16-bit data bus /LWR Lower byte-lane write strobe /UWR Upper byte-lane write strobe /CAS_0 Read strobe /CE_0 (in) Cartridge ROM /CS from system /CE_0 (out) Cartridge ROM /CS to Game Genie cartridge connector /MRES Cold boot reset signal /VRES Warm boot reset signal VCLK Clock synchronous with the target system CPU In the Genesis some of these signals are generated by the VDP and are equivalent to: /LWR = !R//W & !LDS; /UWR = !R//W & !UDS; /CAS_0 = !AS & R//W & A:[000000..DFFFFF]; /CE_0 = !AS & A:[000000..3FFFFF]; This allows it to detect 23 bits of a 24-bit address range and supply 16-bit replacement data for a given address; it cannot patch byte locations. The current CPU address is compared to the six patch address registers as soon as /CE_0 goes low, regardless of the read or write strobes. If there is a match or multiple matches, the lowest number register set is used to supply the patch data (e.g. if codes 1,3,4 matched, 1 is used). On a successful compare, /CE_0 OUT is deasserted so the cartridge ROM will not respond to the memory access in progress. The ASIC will output the patch data as soon as /CAS_0 goes low; if it doesn't the ASIC will wait wait until the current cycle is terminated (/CE_0 high) and the next one starts where the entire process repeats anew. ---------------------------------------------------------------------------- ASIC registers ---------------------------------------------------------------------------- The ASIC has 20 internal word-sized registers which can be read and written. $0000 = Mode control Bit 10 : Mode bit 1 Bit 9 : Mode bit 0 Bit 8 : Lock bit (1= register writes ignored, 0= register writes accepted) Bit 5 : Code #6 enable (1= on, 0= off) Bit 4 : Code #5 enable (1= on, 0= off) Bit 3 : Code #4 enable (1= on, 0= off) Bit 2 : Code #3 enable (1= on, 0= off) Bit 1 : Code #2 enable (1= on, 0= off) Bit 0 : Code #1 enable (1= on, 0= off) The two mode bits control what is enabled when reading from the cartridge ROM area (when ASIC /CE_0 is asserted): D10 D9 0 0 : Game Genie ROM 0 1 : ASIC register 1 x : Cartridge ROM When the lock bit is set all registers cannot be updated by a write, and this state persists until a cold or warm boot happens. The Game Genie software sets the lock bit before starting a game so the game software cannot change the registers inadvertantly or purposefully. Mode register values used by the Game Genie software are: 0000 : Enable Game Genie ROM. 0200 : Enable ASIC registers. 0600 : Enable cartridge ROM. 07xx : Enable cartridge ROM, codes, and lock ASIC before starting game. $0002 = Reset status Bit 0 : 1= Register had previously been written to. 0= Register has not yet been written to. From a cold boot this bit is reset to zero. When any value is written to this register bit 0 will be set and remain that way through subsequent resets until the power is cycled again. Its purpose is to allow the Game Genie software to tell if this is the first time it is running or not. $0004 = Code #1 patch address, bits 15-1 $0006 = Code #1 patch address, bits 23-16 $0008 = Code #1 patch data, bits 15-0 $000A = Code #2 patch address, bits 15-1 $000C = Code #2 patch address, bits 23-16 $000E = Code #2 patch data, bits 15-0 $0010 = Code #3 patch address, bits 15-1 $0012 = Code #3 patch address, bits 23-16 $0014 = Code #3 patch data, bits 15-0 $0016 = Code #4 patch address, bits 15-1 $0018 = Code #4 patch address, bits 23-16 $001A = Code #4 patch data, bits 15-0 $001C = Code #5 patch address, bits 15-1 $001E = Code #5 patch address, bits 23-16 $0020 = Code #5 patch data, bits 15-0 $0022 = Code #6 patch address, bits 15-1 $0024 = Code #6 patch address, bits 23-16 $0026 = Code #6 patch data, bits 15-0 The following are read-only mirrors due to the way the internal register addresses are decoded. Writes do not update the addressed register. $0028 = Code #5 patch address, bits 15-1 $002A = Code #5 patch address, bits 23-16 $002C = Code #5 patch data, bits 15-0 $002E = Code #6 patch address, bits 15-1 $0030 = Code #6 patch address, bits 23-16 $0032 = Code #6 patch data, bits 15-0 $0034 = Code #5 patch address, bits 15-1 $0036 = Code #5 patch address, bits 23-16 $0038 = Code #5 patch data, bits 15-0 $003A = Code #6 patch address, bits 15-1 $003C = Code #6 patch address, bits 23-16 $003E = Code #6 patch data, bits 15-0 ---------------------------------------------------------------------------- Sega Genesis Game Genie ---------------------------------------------------------------------------- The Genesis Game Genie conists of the SGA001 ASIC and two 28-pin mask ROMs that are equivalent to a 27C128. They contain the Game Genie program which is run from a cold boot. Up to five codes can be entered, the 6th is reserved for patching the checksum stored in the cartridge header. It is used depending on the cartridge header: - 'SEGA' text present, checksum in header matches calaculated checksum. New checksum is calculated after codes are enabled and address $018E is patched with the new checksum. - 'SEGA' text present, checksum in header doesn't match calculated checksum. The header checksum is not patched. This is for games where the checksum calculation method differs which the Game Genie can't account for, or possibly bootleg games with modified program code but the original checksum. - 'SEGA' text not present. The header checksum is not patched. This is for unlicensed games with no header or checksum. The latter two cases would free up the sixth code, but an option isn't given to use it. Nearly all Genesis games need a "master code" to work, I suspect this was done because Sega either changed their checksum calculation to defeat the Game Genie or the original calculation method was incorrect. I would assume the Game Genie program could be updated to support other checksum methods, but there doesn't seem to be any evidence this was done. There seem to be some functions left over from development. A subroutine at $1982 (loaded into RAM at $194C) enables cartridge ROM and checks if the string "GAME GENIE TEST " exists at offset $0100. If it does, the mode is set to $00 and codes are disabled, then it jumps to the cartridge entry point. Otherwise the subroutine returns to the main program. This would allow a cartridge to be plugged into the Game Genie which stores an in-development version of the Game Genie software to be tested. A production Game Genie has the Sega system identifier string ("SEGA MEGA DRIVE ") at $0100 instead. Basic memory map 000000-007FFF : Game Genie ROM 000000-00003F : ASIC registers (write, can read when mode=$02) ---------------------------------------------------------------------------- Super Nintendo Game Genie ---------------------------------------------------------------------------- Overview ~~~~~~~~ The Super Nintendo Game Genie consists of the following components: U1 : SGA001 ASIC U2 : 28-pin mask ROM, 27C512 equivalent U3 : "GALPALS-1" 16V8 PAL U4 : 74LS30 8-input NAND to detect memory accesses to bank $FF. U5,U8 : 74LS377 positive edge triggered 8-bit latches with latch-enable input U6,U7 : 74LS244 buffers Most of the extra hardware is used to interface the ASIC to an 8-bit bus. The ASIC is connected to the CPU as follows: - S-CPU A15 is not connected. The ASIC cannot determine the state of A15 when patching an address: LoROM games do not have A15 connected to the cartridge ROM either so no problems occur. HiROM games use A15, meaning within a 64K HiROM bank the Game Genie will patch both addresses specified. For example: ASIC register CPU address patch address that is patched ------------- --------------- $4003F0 $40:$03F0 (CPU A15=0) and $40:$83F0 (CPU A15=1) $C003F0 $C0:$03F0 (CPU A15=0) and $40:$83F0 (CPU A15=1) Presumably the SNES Game Genie was designed with LoROM in mind. HiROM banks can be patched if the opposite address of the patch address is not critical. Or if a game program accessed the upper 32K of a bank mirrored in the LoROM area, it could be properly patched but I doubt many games do that. - S-CPU A0 is connected to ASIC A15. This allows the ASIC to patch byte addresses rather than word addresses. Some examples: ASIC register CPU address patch address that is patched ------------- --------------- $000000 $00:8000 $008000 $00:8001 $000002 $00:8002 $008002 $00:8003 $400000 $40:0000 and $40:8000 $408000 $40:0001 and $40:8001 $400002 $40:0002 and $40:8002 $408002 $40:0003 and $40:8003 - ASIC D7-D0 are not connected to the data bus, only D15-D0 are. During a patch operation, only the MSB of the replacement data is output. The LSB can be read through some additional hardware, this isn't intended for patching but rather for access to all 16-bits of the word-sized ASIC registers. Game Genie software ~~~~~~~~~~~~~~~~~~~ Only five codes can be entered despite six being supported by the hardware. This is not a limitation of the SNES, the 6th code functions normally. It seems to be simply how the software was written. There seem to be some functions left over from development. An unused subroutine writes to the ASIC at $FF810x to enable cartridge ROM and checks if the text "GAME GENIE TEST " exists at $FFC0. If it does, codes are disabled, the mode is set to $00 (Game Genie ROM enabled) and it jumps to the cartridge entry point. Otherwise the subroutine exits cleanly. This would allow a cartridge to be plugged into the Game Genie which stores an in-development version of the Game Genie software to be tested A production Game Genie has the Nintendo specified product name string ("Game Genie ") at $FFC0 instead. ASIC writes ~~~~~~~~~~~ The ASIC 16-bit data bus is connected as follows: ASIC D15-D8 <- S-CPU D7-D0 ASIC D7-D0 <- WRBUF <- WRLATCH <- S-CPU D7-D0 WRBUF is a octal buffer with output enable, WRLATCH is a octal latch. They are enabled for the following address accesses: (/ROMSEL=L and /WR=L) ASIC ASIC WRBUF WRLATCH MSB LSB /LWR /UWR /OE /G ---- ---- ---- ---0 ---- ---0 : H H H L ---- ---- ---- ---0 ---- ---1 : L L L H ---- ---- ---- ---1 ---- ---0 : H L H H The write latch is loaded when: * Writing to an even ROM address in page 0 of a bank. ASIC /LWR is asserted when: * Writing to an odd ROM address in page 0 of a bank. ASIC /UWR is asserted when: * Writing to an odd ROM address in page 0 of a bank. * Writing to an even ROM address in page 1 of a bank. ASIC D7-D0 is driven with the latched write data when: * Writing to an odd ROM address in page 0 of a bank. Examples of addresses used would be: (when M=0,X=0) sta $8000.w Load LSB latch with CPU data sta $8001.w Write word to ASIC; D15-D8= Latched data, D7-D0 = CPU data sta $8100.w Write byte to ASIC; D15-D7= CPU data NOTE: The bank used is not significant. The Game Genie software uses the $80xx range to write word data to registers and $810x to write byte data to the MSB of registers. ASIC reads ~~~~~~~~~~ The ASIC 16-bit data bus is connected as follows: ASIC D15-D8 -> S-CPU D7-D0 ASIC D7-D0 -> RDLATCH -> RDBUF -> S-CPU D7-D0 RDBUF is a octal buffer with output enable, RDLATCH is a octal latch. They are enabled for the following address accesses: (/ROMSEL=L and /RD=L) ASIC RDBUF RDLATCH MSB LSB /RD /OE /G 1111 1111 ---- ---0 ---- ---1 : H L H 1111 1111 ---- ---0 ---- ---0 : L H L nnnn nnnn ---- ---1 ---- ---- : L H H (any bank but $FF) ---- ---- ---- ---0 ---- ---0 : L H H nnnn nnnn ---- ---0 ---- ---1 : L H H (any bank but $FF) ASIC /RD is asserted when: * Reading from an even ROM address in page 0 of a bank. * Reading from a ROM address in page 1 of any bank but $FF. * Reading from an odd ROM address in page 0 of any bank but $FF S-CPU D7-D0 is driven with the latched read data when: * Reading from an odd ROM address in page 0 of bank $FF. (Only when the Genie ROM and cartridge ROM are disabled, e.g. mode=$02) The read latch is loaded when: * Reading from an even ROM address in page 0 of bank $FF. Examples of addresses used would be: (when M=0,X=0) 1.) lda $008000.l Load A with ASIC D15-D8 2.) lda $008001.l Load A with ASIC D15-D8 3.) lda $008100.l Load A with ASIC D15-D8 4.) lda $008101.l Load A with ASIC D15-D8 5.) lda $FF8000.l Load A with ASIC D15-D8 and read latch with ASIC D7-D0 6.) lda $FF8001.l Load A with read latch data 7.) lda $FF8100.l Data bus isn't driven by anything (read $FF) 8.) lda $FF8101.l Data bus isn't driven by anything (read $FF) 1-4 show that reading any ROM location not in bank $FF returns the MSB of ASIC data *without* loading the read latch. Apart from providing a way for the Game Genie program to read the MSB of a register without altering the read latch, these are 'pass-through' addresses normally used for the patch data to be read when a game runs. 5-6 show that reading ROM locations in page 0 of bank $FF will return the MSB of ASIC data at even addresses while loading the read latch, and the latched data from the addressed register at odd addresses. These are commonly used for word reads from ASIC registers. 7-8 show that for whatever reason any odd page in bank $FF has all devices disabled. This is unallocated address space that is not responded to by anything. Data returned is always $FF in this case though different loading of the data bus could change that value. This configuration means patching bank $FF (HiROM bank, normally not patched) has some additional complications: 1. Patching offsets in odd pages of bank $FF ASIC /RD is disabled when reading any offset in an odd page in bank $FF. If an address within that range is patched, the ASIC will disable cartridge ROM when the patch address matches the CPU address as expected, but it will never output any data. Since no other devices respond, the data returned is always $FF regardless of the patch data specified. (see note about data bus loading above for why the value is $FF) 2. Patching offsets in even pages of bank $FF When an even address is patched everything functions normally: the ASIC will match the address and as ASIC /RD is strobed the MSB of the patch data is returned. Because the bank is $FF this also enables the read latch will latches the LSB of the patch data; the part that is normally not used. When an odd address is patched the ASIC disables cartridge ROM when the address matches the patch address. However ASIC /RD is disabled. Because the requirements to enable the read buffer are set up, it outputs the contents of the read latch instead. This data is whatever the last byte loaded into the read latch was. For example if address $FF:8200 is patched with $AA56, and $FF:8201 is patched with $BB78, a CPU read from $FF:8200 returns patch data $AA and loads the latch with $56. Next, reading $FF:8201 returns the latch data which was last set to $56, and the ASIC at no point drives its data bus with $BB78 as ASIC /RD was never asserted. These two cases are side effects of bank $FF being used for ASIC register access rather than a specific feature of the SNES Game Genie implementation. SNES Game Genie programming summary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Memory map 00:8000-00:FFFF : First 32K of Game Genie ROM 01:8000-01:FFFF : Latter 32K of Game Genie ROM 00:8000-00:803F : ASIC registers, write address for word data 00:8100-00:813F : ASIC registers, write address for byte data (MSB only) 00:8000-00:803F : ASIC registers, read address for byte data (MSB only) (when mode=$02) FF:8000-FF:803F : ASIC registers, read address for word data (when mode=$02) FF:8100-FF:813F : ASIC registers, write address for byte data (MSB only) Reading always returns $FF rather than the ASIC register data. Example uses: lda $8000.w ; (M=0,X=0) Get mode bits (MSB) in A from mode register lda $FF8000.l ; (M=0,X=0) Get code-enable bits (LSB) in A from mode register sta $8026.w ; (M=1,X=0) Store A to code #6 data register (word data) sta $8126.w ; (M=0,X=0) Store A to code #6 data MSB only (register LSB not changed) lda $FF8100.l ; (M=0,X=0) Read $FF into A Notes - ASIC data is big-endian format due to its 68000-centric architecture. Even bytes are MSBs, odd bytes are LSBs. - Bit 15 of the patch address is really CPU address bit 0, and likewise CPU address bit 15 is omitted from the patch address entirely. ---------------------------------------------------------------------------- ASIC pin assignments ---------------------------------------------------------------------------- SGA001, 68-pin PLCC Assumes clockwise pin ordering from pin 1. Pin Genesis connections SNES connections 61 : GND GND 62 : 68K D15 S-CPU D7 63 : 68K D14 S-CPU D6 64 : 68K D13 S-CPU D5 65 : 68K D12 S-CPU D4 66 : +5V +5V 67 : GND GND 68 : 68K D11 S-CPU D3 *01 : 68K D10 S-CPU D2 02 : 68K D9 S-CPU D1 03 : 68K D8 S-CPU D0 04 : +5V +5V 05 : GND GND 06 : 68K D7 WRBUF D7 / RDLATCH D7 07 : 68K D6 WRBUF D6 / RDLATCH D6 08 : 68K D5 WRBUF D5 / RDLATCH D5 09 : 68K D4 WRBUF D4 / RDLATCH D4 10 : +5V +5V 11 : GND GND 12 : 68K D3 WRBUF D3 / RDLATCH D3 13 : 68K D2 WRBUF D2 / RDLATCH D2 14 : 68K D1 WRBUF D1 / RDLATCH D1 15 : 68K D0 WRBUF D0 / RDLATCH D0 16 : +5V +5V 17 : GND GND 18 : From code enable switch From code enable switch 19 : VCLK (7.67 MHz) SYSCK (3.58 MHz) 20 : /MRES From RC ckt. near switch 21 : /VRES /RESET 22 : GND GND 23 : GENIE ROM(s) /CS To GENIE ROM /CS 24 : /LWR From GALPALS-1 pin 15 25 : /UWR From GALPALS-1 pin 18 26 : +5V +5V 27 : GND 28 : /CAS_0 IN (68K /RD from VDP) From GALPALS-1 pin 19 29 : /CE_0 IN (68K /CS from VDP) /ROMSEL (ROM /CS from S-CPU) 30 : /CE_0 OUT To GALPALS-1 pin 1 31 : 68K A23 S-CPU A23 32 : 68K A22 S-CPU A22 33 : 68K A21 S-CPU A21 34 : GND GND 35 : 68K A20 S-CPU A20 36 : 68K A19 S-CPU A19 37 : 68K A18 S-CPU A18 38 : +5V +5V 39 : 68K A17 S-CPU A17 40 : 68K A16 S-CPU A16 41 : 68K A15 S-CPU A0 (!) 42 : GND GND 43 : 68K A14 S-CPU A14 44 : 68K A13 S-CPU A13 45 : 68K A12 S-CPU A12 46 : 68K A11 S-CPU A11 47 : 68K A10 S-CPU A10 48 : GND GND 49 : +5V +5V 50 : 68K A9 S-CPU A9 51 : 68K A8 S-CPU A8 52 : 68K A7 S-CPU A7 53 : 68K A6 S-CPU A6 54 : GND GND 55 : 68K A5 S-CPU A5 56 : 68K A4 S-CPU A4 57 : 68K A3 S-CPU A3 58 : 68K A2 S-CPU A2 59 : 68K A1 S-CPU A1 60 : +5V +5V Pin 19 is a clock signal synchronous with the CPU. (VCLK is the 68000 clock, SYSCK is the S-CPU clock) Pin 20 is a master reset signal that is only asserted during a cold boot. The SNES has no such signal so a RC reset circuit is connected to this pin. ---------------------------------------------------------------------------- SNES Game Genie pin assignments ---------------------------------------------------------------------------- U3 - "GALPALS-1", 20-pin DIP +----v----+ /ASIC CE_0 OUT (p30)I0 |01 i s 20| +5V +5V /ASIC CE_0 I1 |02 i o 19| B7 ASIC /RD /NAND_Y I2 |03 i o 18| B6 ASIC /UWR S-CPU SYSCK I3 |04 i o 17| B5 WRLATCH CLK, RDLATCH CLK S-CPU A0 I4 |05 i o 16| B4 RDLATCH /G S-CPU A8 I5 |06 i o 15| B3 ASIC /LWR, WRBUF /1G, /2G S-CPU A15 I6 |07 i o 14| B2 WRLATCH /G S-CPU /ROMSEL I7 |08 i o 13| B1 RDBUF /1G, /2G S-CPU /RD I8 |09 i o 12| B0 Cartridge /ROMSEL GND GND |10 s i 11| I9 S-CPU /WR +---------+ U6 - LS244 +----v----+ GALPALS-1 p13 /1G |01 20| +5V +5V WD0 1A1 |02 19| /2G GALPALS-1 p13 CPU D7 2Y4 |03 18| 1Y1 CPU D0 WD1 1A2 |04 17| 2A4 WD7 CPU D6 2Y3 |05 16| 1Y2 CPU D1 WD2 1A3 |06 15| 2A3 WD6 CPU D5 2Y2 |07 14| 1Y3 CPU D2 WD3 1A4 |08 13| 2A2 WD5 CPU D4 2Y1 |09 12| 1Y4 CPU D3 GND GND |10 11| 2A1 WD4 +---------+ U5 - LS377 +----v----+ GALPALS-1 p16 /G |01 20| +5V +5V WD0 1Q |02 19| 8Q WD7 ASIC D0 1D |03 18| 8D ASIC D7 ASIC D1 2D |04 17| 7D ASIC D6 WD1 2Q |05 16| 7Q WD6 WD2 3Q |06 15| 6Q WD5 ASIC D2 3D |07 14| 6D ASIC D5 ASIC D3 4D |08 13| 5D ASIC D4 WD3 4Q |09 12| 5Q WD4 GND GND |10 11| CLK GALPALS-1 p17 +---------+ U7 - LS244 +----v----+ GALPALS-1 p15 /1G |01 20| +5V +5V RD0 1A1 |02 19| /2G GALPALS-1 p15 ASIC D7 2Y4 |03 18| 1Y1 ASIC D0 RD1 1A2 |04 17| 2A4 RD7 ASIC D6 2Y3 |05 16| 1Y2 ASIC D1 RD2 1A3 |06 15| 2A3 RD6 ASIC D5 2Y2 |07 14| 1Y3 ASIC D2 RD3 1A4 |08 13| 2A2 RD5 ASIC D4 2Y1 |09 12| 1Y4 ASIC D3 GND GND |10 11| 2A1 RD4 +---------+ U8 - LS377 +----v----+ GALPALS-1 p14 /G |01 20| +5V +5V RD0 1Q |02 19| 8Q RD7 CPU D0 1D |03 18| 8D CPU D7 CPU D1 2D |04 17| 7D CPU D6 RD1 2Q |05 16| 7Q RD6 RD2 3Q |06 15| 6Q RD5 CPU D2 3D |07 14| 6D CPU D5 CPU D3 4D |08 13| 5D CPU D4 RD3 4Q |09 12| 5Q RD4 GND GND |10 11| CLK GALPALS-1 p17 +---------+ U2 - Mask ROM (27C512 eq.) +----v----+ CPU A16 Vpp |01 28| +5V +5V CPU A12 A12 |02 27| A14 CPU A14 CPU A7 A7 |03 26| A13 CPU A13 CPU A6 A6 |04 25| A8 CPU A8 CPU A5 A5 |05 24| A9 CPU A9 CPU A4 A4 |06 23| A11 CPU A11 CPU A3 A3 |07 22| /OE CPU /RD CPU A2 A2 |08 21| A10 CPU A10 CPU A1 A1 |09 20| /CS /ASIC CE_0 CPU A0 A0 |10 19| D7 CPU D7 CPU D0 D0 |11 18| D6 CPU D6 CPU D1 D1 |12 17| D5 CPU D5 CPU D2 D2 |13 16| D4 CPU D4 GND GND |14 15| D3 CPU D3 +---------+ ---------------------------------------------------------------------------- End ----------------------------------------------------------------------------