{"id":17175073,"url":"https://github.com/grenderg/supervision_reveng_notes","last_synced_at":"2026-02-06T21:02:00.712Z","repository":{"id":86527619,"uuid":"221804369","full_name":"GrenderG/supervision_reveng_notes","owner":"GrenderG","description":"Watara Supervision Reverse Engineering Notes","archived":false,"fork":false,"pushed_at":"2019-11-14T23:26:44.000Z","size":28,"stargazers_count":12,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-07-29T20:36:41.087Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GrenderG.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-11-14T23:23:43.000Z","updated_at":"2025-03-26T05:48:25.000Z","dependencies_parsed_at":null,"dependency_job_id":"ef15eb7c-cec8-49fe-a934-c411682ac7ac","html_url":"https://github.com/GrenderG/supervision_reveng_notes","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/GrenderG/supervision_reveng_notes","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GrenderG%2Fsupervision_reveng_notes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GrenderG%2Fsupervision_reveng_notes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GrenderG%2Fsupervision_reveng_notes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GrenderG%2Fsupervision_reveng_notes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GrenderG","download_url":"https://codeload.github.com/GrenderG/supervision_reveng_notes/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GrenderG%2Fsupervision_reveng_notes/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29175824,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-06T20:14:21.878Z","status":"ssl_error","status_checked_at":"2026-02-06T20:14:21.443Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T23:55:39.138Z","updated_at":"2026-02-06T21:02:00.693Z","avatar_url":"https://github.com/GrenderG.png","language":null,"readme":"Supervision Reverse Engineering Notes\n-------------------------------------\n\n*This is a copy of http://blog.kevtris.org/blogfiles/Supervision_Tech.txt just to preserve it.*\n\n\n\u003cpre\u003e\nSupervision Reverse Engineering Notes\n-------------------------------------\n\nKevin Horton\n12/20/2011\nV4.00\n\n\n\n\nThe Supervision is a Gameboy wanna-be.  It was sold and marketed in many countries, under many different names.  Here in the US, it was simply sold as the \"Supervision\".  The branding on it is \"Watara\", and it has a vaguely Gameboy-like look, but the screen can be angled up or down via a hinge below the display proper.\n\nThe top half contains the actual game hardware, a 160*160 LCD, the main PCB with a glop-top ASIC which is the \"brains\", and two 8K SRAMs.  The two 8K SRAMs are 6264's or equivelant, and are physically glop-tops on little tiny carrier boards.\n\nThe cartridges seem to support up to 128K byte ROMs, but as far as I know, the games were only as large as 64K bytes.  There are a few 4 in 1 multicarts which might be 128K bytes, but contain games that were previously released on separate carts.\n\nTo perform this reverse engineering, I made a special test system.  It is composed of a home made game cartridge, with a UART mapped into memory at 1FFE and 1FFF.  I cut the chip enable to the internal WRAM, and ran it through a circuit to pull out 1FFE/1FFF, which then ran the chip enable on the UART.  \n\nA simple \"bios\" was made that allowed me to read/write data to/from anywhere in memory.  This was then used to poke data into VRAM, poke registers, and all that fun stuff.  I used an HP 54645D mixed signal oscilloscope/logic analyzer.  I connected a test header to the VRAM and LCD control signals.  This was then used to poke at things on the system to figure out how it all works.\n\nSo that's the background, now onto the juicy technical details.\n\n---\n\nSystem Overview\n---------------\n\nThe device runs on a 4MHz resonator.  This resonator's not that great (mine was 1.35% off), so I replaced it with a 4MHz crystal.  I replaced to increase the accuracy which helped during reverse engineering on the logic analyzer.\n\nThere are not many parts in this system:\n\n* 65C02 CPU running at 4MHz\n* 8K of VRAM which the CPU can access with 0 wait states\n* 8K of WRAM which is connected to the CPU\n* audio amplifier\n* 160*160 monochrome LCD (see the LCD section)\n* speaker\n* cartridge port that can accept a single ROM, up to 128Kbytes\n\n---\n\nThe LCD system\n--------------\n\nThe LCD on the Supervision is a simple 160*160 dot 2 bpp monochrome LCD.  The LCD uses some tricks to load the data into it.  A full frame is composed of two fields.  Each field is 1 bit per pixel, so to show a full frame it takes two fields.\n\nframe:  p0 p1 p2 p3\n-------------------\n1       0  0  1  1\n2       0  1  0  1\n3       0  0  1  1\n4       0  1  0  1\n\np0 is an 'off' pixel.  It shows up as the lightest colour possible\np1 1/3rd darkness\np2 2/3rd darkness\np3 an 'on' pixel which is as dark as possible\n\nThe LCD is physically connected with 9 outputs from the ASIC.  \nIn order, they are:\n\n1 - Data 0\n2 - Data 1\n3 - Data 2\n4 - Data 3\n5 - Pixel Clock\n6 - Line Latch\n7 - Frame Latch\n8 - Frame Polarity/bright control\n9 - Power Control\n\nA diagram of how the LCD hardware works is probably in order.  The LCD on this system is not much different from other similar displays available from Optrex and other manufacturers. \n\n\nA block diagram of the LCD system looks like this:\n\n                   +---------------------------+\n      Pix clk(5)O--|Clock                      |\n       Data 0(1)O--|D0          CK LT PL       | \n       Data 1(2)O--|D1    4*40 bit shift reg   |\n       Data 2(3)O--|D2                         |\n       Data 3(4)O--|D3 0...            ...159  |\n                   +---------------------------+\n                     |                        |\n                     |                        |\n                   +---------------------------+\n                   |                           |\n     Line Lat(6)O--|Latch  160 bit latch       |\n     Polarity(8)O--|Pol                        |\n                   +---------------------------+ \n                     |                        |\n                     |       160 Columns      |\n                  +------------------------------+\n                  |                              |\n        +-----+   |                              |\nLL(6)O--|CK  0|---|                              |\nFL(7)O--|DT  .|   |                              |\nPL(8)O--|PL  .| 1 |                              |\n        |    .| 6 |          LCD Glass           |\n        |     | 0 |                              |\n        |     |   |                              |\n        |     | R |                              |\n        |     | o |                              |\n        |     | w |                              |\n        |     | s |                              |\n        |    .|   |                              |\n        |    .|   |                              |\n        |    .|   |                              |\n        |  159|---|                              |\n        +-----+   |                              |\n                  |                              |\n                  +------------------------------+\n\nThere are two shift registers.  Data is shifted into the column shift register 4 bits at a time. This is done simply to increase the speed at which data can be shifted in.  The shift register is pretty simple, and bits are arranged on the 160 pin outputs in sequence like so:\n\n01230123012301230123....  (0,1,2,3 = data bits 0-3).\n\nAfter 40 clocks (160 pixels:  40*4 = 160), the line latch signal is pulsed, which latches all 160 pixels into the columns of the LCD glass at the same time.\n\nThere is a second shift register that drives the rows of the LCD.  This shift register selects a single row to be enabled.  Its data input is connected to the frame latch signal:  It's pulsed high for 1 scanline, and on line latch's falling edge, it's clocked into the row shift register.\n\nThis enables the topmost scanline, and when it is enabled, the first row's pixel signals are simultaniously latched into the column drivers.  This \"lights up\" the uppermost row of pixels.\n\nThe next line latch signal then clocks the row shift register and simultaniously latches in new data into the columns to \"light up\" the second row of pixels, and so on.  This process repeats for all 160 scanlines, and repeats.\n\nThe frame polarity/bright control signal is toggled every field.  This inverts all the driver signals.  It also darkens the display a bit so you can get a true 2 bits per pixel. The polarity toggling is done to prevent destruction of the LCD display glass via electrolytic plating action.  (Incidentally, this is why you should never turn the Gameboy LCD off during rendering and you must turn it off in VBLANK).\n\nTiming\n------\n\nA single frame is composed of two fields.  A field is a complete scanning of the LCD.  There's two fields in each frame to effect the grey scale.\n\nEach field takes exactly 39360 cycles.  A complete frame is thus 2x that, or 78720 cycles.\nThere are 160 scanlines in each field, and each scanline takes 246 clocks to complete.  Thus, 246*160 = 39360, and 39360*2 = 78720.  With the 4MHz system clock, this means that there are 101.62 fields a second, or 50.81 frames a second.\n\nEach scanline is composed of 246 clocks.  There are 40 pixel writes to the LCD, and 1 latch write, for a total of 41 writes.  Each write period lasts 6 clock cycles, so 41*6 = 246 cycles.\n\nOn the following diagram, a clock cycle is 1 character cell. The \"clk cyc\" is a count of the phase of the LCD write hardware, which performs 1 write to the LCD every 6 clocks.  I have arbitrarily placed the pixel clock (which writes 4 pixels into the LCD shift register) on clock cycle 0.\n\nI have shown a line latch pulse on the diagram, too.  These pulses only occur every 246 clocks, and are what latch data into the LCD's output buffers from the LCD's input shift register.\n\nclk cyc  012345012345012345012345012345012345012345012345012345012345012345012345\npixclk   -_____-___________-_____-_____-_____-_____-_____-_____-_____-_____-_____\ndata 0-3 ====x=====x=====x=====x=====x=====x=====x=====x=====x=====x=====x=====x=\nline lat _____________-__________________________________________________________\n\n_ - a low signal\n- - a high signal\n= - data (high or low)\nx - transition point (where this signal CAN (but doesn't have to) change\n\nscanline timing:\n\n                |       field N            |      field N+1         | field N+2 ...\nframe lat ______-______________/___________-____________/___________-________\nframe pol ______---------------/-----------_____________/___________---------\n\nEach character cell is 1 scanline (246 clocks).  There are 160 scanlines represented on the diagram, with a / to indicate that part of the diagram has been removed for clarity.\n\nThat about wraps up LCD timing.  The LCD timing does not deviate from this set pattern, even when the CPU writes or reads from the VRAM.  \n\n\n---\n\nVRAM Timing\n-----------\n\nThe VRAM timing is very rigid and does not deviate, even during CPU reads or writes.  The RAM is simply addressed by the video hardware and sent out to the LCD.\n\nEvery 6 clocks, a new RAM location is accessed.  The address lines update every 6 cycles.  This corresponds neatly to the LCD output timing.  The way the data gets from the RAM to the LCD is very simple.  Data is read a byte at a time from the VRAM, and 4 bits of it are sent to the 4 LCD data lines.  Either D0/D2/D4/D6 or D1/D3/D5/D7 depending on which field it is.  The bits sent alternate this way every field to effect the 4 level greyscale.  Thus, every 2 bits of RAM correspond to a single pixel on the LCD.\n\nWhen the CPU reads or writes the VRAM, it simply inserts a single clock access.  This access can occur anywhere in the 6 cycle period.  The video controller seems to read the LCD data on all 6 clocks, and any clock where there is a CPU access, it simply doesn't read it.  This means that unless there's 6 writes or reads in a row from the CPU, it will always have valid display data.  During normal operation, there is no way there can be more than 1 write to VRAM at any one time. (An interrupt or JSR would do 3 or 2 respectively, but VRAM does not sit at 0100h so this can never occur). A fun test will be to execute code out of VRAM to see how the display reacts.\n\n---------------------------------------\n\n\nCartridge pinout:\n\n(taken from svision.txt from the Mess emulator documentation, updated by me.)\n\nCartridge connector (looking at the cartridge pins)\n            +-------+\n       /RD -| 1  40 |- +5v (picture side)\n        A0 -| 2  39 |- nc\n        A1 -| 3  38 |- nc\n        A2 -| 4  37 |- nc\n        A3 -| 5  36 |- nc\n        A4 -| 6  35 |- /WR\n        A5 -| 7  34 |- D0\n        A6 -| 8  33 |- D1 \n        A7 -| 9  32 |- D2\n        A8 -| 10 31 |- D3\n        A9 -| 11 30 |- D4\n       A10 -| 12 29 |- D5\n       A11 -| 13 28 |- D6\n       A12 -| 14 27 |- D7\n       A13 -| 15 26 |- nc\n       A14 -| 16 25 |- nc\n       A15 -| 17 24 |- nc\n       A16 -| 18 23 |- nc\n        nc -| 19 22 |- gnd\n        nc -| 20 21 |- power ground\n            +-------+\n\n* A0-A16 connect to A0-A16 on the ROM/EPROM\n* D0-D7 connect to D0-D7 on the ROM/EPROM\n* /RD connects to /CE or /OE (or both) on the EPROM\n* /WR is not used (but could be used to run a simple latch to effect more bankswitching)\n* power ground must be connected to gnd for the Supervision to turn on\n\nThat power ground input connects from the power source to ground on the rest of the console. The system will not power up without a cartridge installed.\n\n\n---------------------------------------\n\nASIC Pinout\n-----------\n\n\n 1 - VRAM D5\n 2 - VRAM D6\n 3 - VRAM D7\n 4 - VRAM /WR\n 5 - Dpad Right\n 6 - Dpad Left\n 7 - Dpad Down\n 8 - Dpad Up\n 9 - B Button\n10 - A Button\n11 - Select Button\n12 - Start Button\n13 - +5V\n14 - Audio Left Out\n15 - Audio Right Out\n16 - GND\n17 - CLK Out?\n18 - CPU D7\n19 - CPU D6\n20 - CPU D5\n21 - CPU D4\n22 - CPU D3\n23 - CPU D2\n24 - CPU D1\n\n25 - CPU D0\n26 - CPU A16\n27 - CPU A15\n28 - CPU A14\n29 - CPU A13\n30 - CPU A12\n31 - CPU A11\n32 - CPU A10\n33 - CPU A9\n34 - CPU A8\n35 - CPU A7\n36 - CPU A6\n37 - CPU A5\n38 - CPU A4\n39 - CPU A3\n40 - CPU A2\n\n41 - CPU A1\n42 - CPU A0\n43 - WRAM /CE\n44 - CPU R/W\n45 - CART /CE\n46 - NC\n47 - Link Port D0\n48 - Link Port D1\n49 - Link Port D2\n50 - Link Port D3\n51 - LCD 9\n52 - LCD 8\n53 - LCD 7\n54 - LCD 6\n55 - LCD 5\n56 - LCD 4\n57 - LCD 3\n58 - LCD 2\n59 - LCD 1\n60 - Xtal 2\n61 - Xtal 1\n62 - GND\n63 - VRAM A0\n64 - VRAM A1\n\n65 - VRAM A2\n66 - VRAM A3\n67 - VRAM A4\n68 - VRAM A5\n69 - VRAM A6\n70 - VRAM A7\n71 - VRAM A8\n72 - VRAM A9\n73 - VRAM A10\n74 - VRAM A11\n75 - VRAM A12\n76 - VRAM D0\n77 - VRAM D1\n78 - VRAM D2\n79 - VRAM D3\n80 - VRAM D4\n\n\n---------------------------------------\n\nThat's the basics of the hardware end.  Now for programming.\n\n\n\n2000 - LCD_X_Size\n2001 - LCD_Y_Size\n2002 - X_Scroll\n2003 - Y_Scroll\n2004 - Mirror of register 2000\n2005 - Mirror of register 2001\n2006 - Mirror of register 2002\n2007 - Mirror of register 2003\n2008 - DMA Source low\n2009 - DMA Source high\n200A - DMA Destination low\n200B - DMA Destination high\n200C - DMA Length\n200D - DMA Control\n200E\n200F\n2010 - CH1_Flow (Right only)\n2011 - CH1_Fhi\n2012 - CH1_Vol_Duty\n2013 - CH1_Length\n2014 - CH2_Flow (Left only)\n2015 - CH2_Fhi\n2016 - CH2_Vol_Duty\n2017 - CH2_Length\n2018 - CH3_Addrlow\n2019 - CH3_Addrhi\n201A - CH3_Length\n201B - CH3_Control\n201C - CH3_Trigger\n201D\n201E\n201F\n2020 - Controller\n2021 - Link port DDR\n2022 - Link port data\n2023 - IRQ Timer\n2024 - Reset IRQ timer flag\n2025 - Reset Sound DMA IRQ flag\n2026 - System Control\n2027 - IRQ Status\n2028 - CH4_Freq_Vol (Left and Right)\n2029 - CH4_Length\n202A - CH4_Control\n202B\n202C - Mirror of 2028\n202D - Mirror of 2029\n202E - Mirror of 202A\n202F\n\n----------------------------------------------------------\n\nAudio system\n------------\n\n2010, 2011, 2012, and 2013 correspond to channel 1 which is a square wave on right channel\n2014, 2015, 2016, and 2017 correspond to channel 2 which is a square wave on left channel\n2018, 2019, 201A, 201B, and 201C correspond to the DMA channel on both output channels\n2028, 2029, 202A correspond to channel 4 which is the noise wave on both output channels\n\nSquare waves:\n-------------\n\nCHx_Flow:\n7       0\n---------\nFFFF FFFF  \n\nF: lower 8 bits of frequency\n\nCHx_Fhi:\n7       0\n---------\n???? ?FFF\n\nF: Upper 3 bits of frequency\n\nCHx_Vol_Duty:\n7       0\n---------\n?EDD VVVV\n\nE: Enable. 1 = channel always produces sound.  0 = only plays when length reg is written\nD: Duty cycle:\n  00 - 12.5%\n  01 - 25%\n  10 - 50%\n  11 - 75%\nV: Volume. 0 = silent, F = loudest.  Linear.\n\nCHx_Length:\n7       0\n---------\nLLLL LLLL\n\nL: Length.  Writing to this register triggers the sound to play for L counts if bit 6 of CHx_Vol_Duty is cleared.  If that E bit is set, the length counter still runs, but it has no effect unless the E bit is cleared before the timer expires.\n\n---\n\nFrequency is calculated using the following formula:\n\n          125000\nFout =  ----------\n         (Freq+1)\n\nFreq is the concatenated value of the 8 lower bits and the 3 upper bits.\n\nWriting to EITHER frequency register resets the duty cycle prescaler, just like writing to Fhi does on the NES... only in this case, writing to either does it.\n\n---\n\nThe length counter works in increments of 16.384ms.  This value is gotten by taking the 4000000Hz clock divided by 65536 via a 16 bit prescaler.  \n\nThere is a 1 clock uncertainty in the length counter, because it's decremented each time the prescaler expires.  This prescaler is never reloaded or reset so the point where the length register is written determines the 1 count uncertainty.\n\nThe length count is simply L+1 * 65536 cycles, with up to 65535 extra cycles added on.  i.e.:\n\nIf L is loaded with 10,  then it will expire between (65536*11) counts and (65536*11+65535) counts.\n\n-------\n\nDMA Channel:\n------------\n\nCH3_Addrlow:\nCH3_Addrehi:\n7       0\n---------\nAAAA AAAA\n\nA: All 16 bits are the start address of the DMA audio channel.  Each register holds 8 bits.\n\nCH3_Length:\n7       0\n---------\nLLLL LLLL\n\nL: Length of the sample to play.  The sample length is L * 16 bytes.  For a length of 00h, 4096 samples will be played (100h * 16).\n\nCH3_Control:\n7       0\n---------\n?BBB LRFF\n\nB: ROM bank to use for pulling sample data\n   These bits determine what ROM bank will be mapped into 8000-BFFF for digi audio.\n   This overrides the bank bits on 2026, but only for digi audio.\nL: Output audio to left channel\nR: Output audio to right channel\nF: Playback frequency: (outputs a sample every...)\n   00 - 256 clocks\n   01 - 512 clocks\n   10 - 1024 clocks\n   11 - 2048 clocks\n\nThe RAM is read at 1/2 the rate samples are outputted.  The memory is read first, then the upper 4 bits are output to the DAC, and then the lower 4 bits are output, then the next byte is read, and the upper 4 bits of the next sample are output, etc.\n\n\nCH3_Trigger:\n7       0\n---------\nT??? ????\n\nT: Trigger the channel. Writing a 1 here will start the sample playing.\n\n\nNotes:\n\nWriting to the length and address registers set them.   When the channel is triggered, via CH3_Trigger, The registers are modified.  Thus, if the channel is triggered again, the address is not updated and points to the last address that was accessed.  Additionally, the length counter is 00h, and the channel plays a maximum length sample (4096 bytes).\n\n-------\n\nNoise Channel:\n--------------\n\nCH4_Freq_Vol:\n7       0\n---------\nFFFF VVVV\n\nV: Volume, 0 = silence, F = maximum,  linear stepping.\n\nF: Frequency:     Divisor:\n   0 - 500KHz     8\n   1 - 125KHz     32\n   2 - 62.5KHz    64\n   3 - 31.25KHz   128\n   4 - 15.625KHz  256\n   5 - 7.8125KHz  512\n   6 - 3.90625KHz 1024\n   7 - 1.953KHz   2048\n   8 - 976.56Hz   4096\n   9 - 488.28Hz   8192\n   A - 244.14Hz   16384\n   B - 122.07Hz   32768\n   C - 61.035Hz   65536\n   D - 30.52Hz    131072\n   E - 61.035Hz   65536\n   F - 30.52Hz    131072\n\n     4000000\nF = ---------\n     divisor\n\nThe frequency is how often (per second) that the noise LFSR is updated.  \n\nThe LFSR is 15 bits long, and is tapped at bits 14 and 15 to generate a maximal length shift register that repeats after 32767 counts.  Bit 0 of CH4_Noise_Con controls the length of the register.  When the bit is clear, the shift register is cut down to just 7 bits, and repeats after 127 counts.  \n\nCH4_Length:\n7       0\n---------\nLLLL LLLL\n\nL: Length to play noise.  Works identically to the square wave length register.\n\nCH4_Control:\n7       0\n---------\n???N LREP\n\nN: Noise Enable\nL: Left output. 1 - mix audio with left channel\nR: Right output. 1 - mix audio with right channel\nE: Channel Enable. 1 - enable noise channel continuously.  0 - use the length register\nP: LFSR length. 1 - 15 bit LFSR, 0 = 7 bit LFSR.\n\nWriting to this register resets the LFSR to all 1's.  Writing to the other noise registers do not reset the LFSR.  \n\n\nAudio Mixing:\n-------------\n\nAudio is mixed in an interesting fashion.  Each of the outputs (left and right) are only 4 bit DACs.  But each channel can output up to 3 different things.  (Square, noise, DMA).\n\nThe hardware simply adds all three together, and clips it at 0fh.  This means that if more than 1 channel is playing, they cannot be outputting the maximum level or else they can be clipped.  Kinda dumb and would result in weird things occuring as well as kinda poor audio output.\n\n----------------------------------------------------------\n\nController\n----------\n\n2020 - Controller\n\n\nNothing too special here.\n\nController:\n7       0\n---------\nSLAB UDLR\n\nS: Start button\nL: Select button\nA: A button\nB: B button\nU: Up on D-pad\nD: Down on D-pad\nL: Left on D-pad\nR: Right on D-pad\n\nPressing a button results in that bit going LOW.  Bits are high for buttons that are not pressed. (i.e. the register returns FFh when no buttons are pressed).\n\n----------------------------------------------------------\n\nLink Port\n---------\n\n2021 - Link port DDR\n2022 - Link port data\n\n\nPinout:\n\nThe link port is a male DB-9 connector.  The pinout is as follows (using standard DB-x pin notations as molded into the plugs/sockets)\n\n1 - D0\n2 - D1\n3 - D2\n4 - D3\n6 - ground\n8 - VCC\n\nAll other pins are NC.\n\n\nLink Port DDR:\n7       0\n---------\n???? DDDD\n\nD: Data direction bits for the link port.\n   0 - this is an output bit\n   1 - this is an input bit\n\nLink Port data:\n7       0\n---------\n???? LLLL\n\nL: Link port bits.\n   0 - output a low on this link port line.\n   1 - output a high on this link port line.\n\nWhen the link port line(s) are set as inputs, they float high via 30uA current sources.  The upper 4 bits on the link port data register returns open bus.\n\nI thought there was a UART on here to communicate on the link port, but it does not appear there is.\n\n\n\n----------------------------------------------------------\n\nDMA\n---\n\n2008 - DMA Source low\n2009 - DMA Source high\n200A - DMA Destination low\n200B - DMA Destination high\n200C - DMA Length\n200D - DMA Control\n\n\nCAUTION:  This DMA can only be used to move data from WRAM/cartridge ROM to VRAM!  Attempting to move data to a non-VRAM address will cause serious problems to occur. See my findings at the bottom of the document in the DMA timing section.\n\n\nDMA Source low/high:\n7       0\n---------\nSSSS SSSS\n   \n   S: Source address\n \nDMA Destination low/high:\n7       0\n---------\nDDDD DDDD\n\n   D: Destination address\n\nThese four registers select the source and destination addresses for the DMA unit.  \n\nDMA length:\n7       0\n---------\nLLLL LLLL\n\n   L: Length*16 (in bytes)\n\nThis register selects how many bytes of data to move.  The actual number of bytes to move is (L * 16).  If the register is loaded with 0, a full 4096 bytes is moved. \n\nAfter a DMA occurs, the registers are updated internally, and performing another DMA will cause a full 4096 bytes to be moved, because the length register now holds 00h.  The source/destination registers are incremented each transfer, and it will start transferring where it left off.\n\nDMA Control:\n7       0\n---------\nS??? ????\n\n   S: Start DMA when written with this bit set.\n\n\n\n\n\n----------------------------------------------------------\n\nInterrupts \u0026 Bankswitching \u0026 System Control\n-------------------------------------------\n\n2023 - IRQ Timer\n2024 - Reset IRQ timer flag\n2025 - Reset Sound DMA IRQ flag\n2026 - System Control\n2027 - IRQ Status\n\n\nNMI:\n\nThe NMI occurs every 65536 clock cycles (61.04Hz) regardless of the rate that the LCD refreshes. I find this interesting, because the LCD refresh is totally divorced from the NMI timing.  In fact, there is no way to know what the LCD is doing from software.\n\nSystem Control:\n7       0\n---------\nBBBS D?IN\n\n   B: Bank select bits for 8000-BFFF.\n   N: Enable the NMI (1 = enable)\n   I: Enable the IRQ (1 = enable)\n   S: IRQ Timer prescaler.  1 = divide by 16384, 0 = divide by 256\n   D: Display enable. 1 = enable display, 0 = disable display  \n\nWriting to this register resets the LCD rendering system and makes it start rendering from the upper left corner, regardless of the bit pattern.\n\nIRQ Timer:\n7       0\n---------\nTTTT TTTT\n\n   T: IRQ Timer.  Readable and writable.  \n\nWhen a value is written to this register, the timer will start decrementing until it is 00h, then it will stay at 00h.  When the timer expires, it sets a flag which triggers an IRQ.  This timer is clocked by a prescaler, which is reset when the timer is written to.  This prescaler can divide the system clock by 256 or 16384.  \n\nWriting 00h to the IRQ Timer register results in an instant IRQ. It does not wrap to FFh and continue counting;  it just stays at 00h and fires off an IRQ.\n\n\nIRQ Status:\n7       0\n---------\n???? ??DT\n\n   D: DMA Audio system (1 = DMA audio finished)\n   T: IRQ Timer expired (1 = expired)\n\nReset IRQ timer flag:\n7       0\n---------\n???? ????\n\nWhen this register is read, it resets the timer IRQ flag (clears the status reg bit too)\n\nReset Sound DMA IRQ flag:\n7       0\n---------\n???? ????\n\nWhen this register is read, it resets the audio DMA IRQ flag (clears status reg bit too)\n\n\nBankswitching\n-------------\n\nThe bankswitching on this system is very simple.  8000-FFFF is the cartridge ROM.  C000-FFFF is fixed to the last 16K bank of the game ROM.  8000-BFFF is selectable using the 3 bits on the System Control register.  0 = first 16K, 1 = second 16K, etc.\n\n\n\n----------------------------------------------------------\n\n\nLCD System\n----------\n\n2000 - LCD_X_Size\n2001 - LCD_Y_Size\n2002 - X_Scroll\n2003 - Y_Scroll\n2004 - Mirror of register 2000\n2005 - Mirror of register 2001\n2006 - Mirror of register 2002\n2007 - Mirror of register 2003\n\n\nFrame Timing Registers\n----------------------\n\n2000: LCD X size\n2001: LCD Y size\n\nLCD X and Y size control LCD refreshing.  All games program A0h / A0h into them since the LCD is 160*160 pixels.\n\nOnly the upper 6 bits of LCD_X_Size are usable, though.  The lower 2 bits are \"don't care\" bits, because the LCD size can only be changed in 4 pixel increments (due to the LCD loading 4 pixels at a time.  See the LCD hardware description above).\n\nChanging LCD_X_size simply changes how many pixclk pulses are output before line latch signal is output.  Frame timing changes of course, depending on how many clocks have been output.  Setting this to a value less than A0h results in some of the columns (in groups of four) NOT being refreshed.  On the system, the columns keep outputting what they were at the time the count was reduced.  So, if the LCD has a black square on it the width of the display and the rest is white, and if the count is reduced when the LCD is scanning the black rows, those columns on the right will be solid black.  If it was scanning the white area at the time, those columns will be solid white.  \n\nIncreasing it past A0h but less than C4h does not do anything visually;  the extra bits just shift out and are not displayed.  It does slow LCD timing down, however.  Increasing it past C3h does do something cool:  the LCD data is shown twice on the screen.  The reason for this is pretty simple.  Every scanline, the VRAM start address is bumped 30h.  This corresponds to 192 pixels.  So setting the count past 192 (C0h) results in it changing the VRAM start address increment size from 30h to 60h.\n\nChanging LCD_Y_Size controls how many scanlines are shown in the frame.  After the requisite number of scanlines, the LCD frame latch signal is output and the frame polarity line is toggled.  Normally, this is A0h for 160 lines.  Making this number larger reduces the LCD duty cycle since each row is on for 1/LCD_Y_Size of the time.  i.e. normally when it's set to A0h, each row is on for 1/160th of the time.  Making it larger reduces the duty cycle further and the display gets dimmer which means you have to advance the contrast knob to see it.  The frame rate also drops because of this.  \n\nDecreasing it below A0h results in a higher duty cycle which means the contrast knob has to be backed off because the whole screen gets darker.  This also has the negative effect of repeating the data on the display.  This is because more than 1 row driver output is on at a time now, so by necessity the bottom scanline(s) will now reflect what's happening at the top of the display.  This also increases the frame rate of the display.\n\nSo, to summerize.  Frame timing is calculated like this:   \n\nScanlinelen = ((LCD_X_Size \u0026 0xFC) + 4) * 6\n\nXsize is anded with FCh because only the upper 6 bits are used, and +4 to account for the line latch pulse, and *6 due to each LCD clocking period taking 6 cycles.\n\nFramelen = Scanlinelen * LCD_Y_Size * 2\n\nFrame length is simply the number of scanlines * the Y_Size register.\n\n---\n\nScrolling:\n----------\n\n2002: X_scroll\n2003: Y_scroll\n\n\nX_scroll and Y_scroll are used to (as you might've guessed) scroll the display information.  The upper 6 bits of X_scroll control the starting offset of the VRAM buffer, and the lower 2 bits delay the bits going to the LCD by 0, 1, 2, or 3 clocks.  The chip actually reads data during the line load pulse to the LCD which reads the first byte of VRAM data.  This data is mostly sent through a 1 deep by 4 bit shift register, which is then run through a multiplexer to choose the appropriate 4 bits to send to the LCD.\n\nY_Scroll is another offset added to the VRAM pointer at the start of the frame.  It's Y_Scroll * 30h.  Since the chip adds 30h (and sometimes 60h, see below) each scanline, this makes sense.  The hardware also checks to make sure that you cannot display the last bit of VRAM, which would offset the pixels, because 48 byte scanlines do not exactly fill all 8K of VRAM.  The closest number is 170.  This leaves 20h bytes on the end which the hardware then skips. (most of the time.  You CAN make it display these.  see below)\n\nTesting:\n--------\n\nFirst, I am setting the LCD_Size registers both to A0h, which is how all games set them.\n\n\nX_scroll is set to 0, and Y_Scroll is set to AAh.  The display looks exactly like it does for a Y_Scroll value of 0, so this tells me the check occurs when the offset used to count through VRAM is updated (either loaded, or 30/60h are added).  Adjusting X_Scroll when Y_Scroll = AAh results in expected behaviour.  If X_Size is changed to a value greater than C3h, this test is still performed, but it might be skipped because of the hardware adding 60h each scanline instead of 30h.  The Y_Scroll still changes the inital offset the same way regardless of X_Size value.\n\nGoing by how this works, I can generate some pseudocode to reproduce the behaviour:\n\n// inputs\nint8  X_Scroll;    // X scroll register\nint8  Y_Scroll;    // Y scroll register\nint8  LCD_X_Size;  // X size register\nint8  LCD_Y_Size;  // Y size register\n\n// internal variables\nint16 Vstart;      // starting VRAM address for the scanline\nint16 Vtemp;       // temporary VRAM address that is incremented in the scanline\nint8  oldpix;      // stores the last 4 pixels read from VRAM\nint16 pixels;      // the current 4 pixels read, and the last 4 pixels read\nint8  outpixels;   // the 4 output pixels\nint8  X;           // X count\nint8  Y;           // Y count\n\n\nVstart = (Y_Scroll * 0x30) \u0026 0x1FFF;      // initial V start value (limit to 8K size)\nif (Vstart == 0x1FE0) Vstart = 0;         // if it has wrapped, perform the wrap here\n\nfor (Y = 1; Y \u003c LCD_Y_Size; Y++)\t\t// do Y_Size scanlines\n{\n    Vtemp = Vstart + (X_Scroll \u003e\u003e 2);\t// start address for this scanline\n    oldpix = buffer[Vtemp];\t\t\t// get the pipeline rolling\n    Vtemp++;\n    for (X = 1; X \u003c (LCD_X_Size \u003e\u003e 2); X++)\n    {\n        pixels = (buffer[Vtemp] \u003c\u003c 8) | oldpix;\t         // mingle old pixels from last read with new ones\n        outpixels = pixels \u003e\u003e ((X_Scroll \u0026 0x03) \u003c\u003c 1);  // get the correct 4 pixels (see below)\n        plotpixels(outpixels);                           // display them\n        oldpix = pixels \u003e\u003e 8;                            // move current pixels into old pixels\n        Vtemp++;\n    }\n    Vstart += ((LCD_X_size \u003e 0xC3) ? 0x60 : 0x30) \u0026 0x1FFF;  // add 30/60 and keep in 8K range\n    if (Vstart == 0x1FE0) Vstart = 0;\n}\n\n\"plotpixels\" is the routine that would plot 4 pixels to the display buffer or LCD or what have you.  The format of the data is simply 4 pixels packed into a byte.  bits 0/1 are one pixel, bits 2/3 are the next, and so on.\n\nThe above code does not take into account clearing/setting the unused pixels on the screen, but I don't think this has to be done for an emulator.\n\n----------------------------------------------------------\n\nDMA Timing\n----------\n\nA bunch of headers were soldered on and I connected up A0-A5 cart /CE, R/W, and my 1ffe/1fff decode output to the logic analyzer.\n\nchannel setup:\n--------------\n0-5 = A0-A5\n6   = cart /CE\n7   = 1FFE/1FFF decode\n8   = R/W\n\nTo test the DMA, I set the DMA start address to FFFF, destination address to 40FF, length to 1 and then started it up and recorded the results on the logic analyzer.\n\nTurns out that the DMA unit fails to operate properly, unless the destination is VRAM.  If the destination is not VRAM, the DMA fails, and ends up clobbering WRAM (if the read pointer is indexing it).  Attempting to DMA VRAM to VRAM causes corruption too, because the data is being overwritten.  \n\nThe DMA is extremely fast.  It copies 5 bytes to the VRAM every 6 clocks;  the 6th clock is for the LCD system to read VRAM for display.\n\nCopying continues until all bytes are copied.  On the idle cycle (every 6 clocks) the CPU is allowed to run for that cycle.\n\n\nAudio DMA Timing\n----------------\n\nNothing special here.   When the Audio DMA Trigger register is written to, a flag is set (I call it \"ADMA playing\").  The first 2 samples are fetched when the prescaler overflows.  The first sample (upper 4 bits of the byte) are then output.  After the prescaler expires again, the lower 4 bits of the byte are output.  After another prescaler expiration, the next byte is fetched, and so on until the complete sample is played.\n\nThe DMA fetch only takes a single cycle to perform.\n\n\u003c/pre\u003e\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrenderg%2Fsupervision_reveng_notes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgrenderg%2Fsupervision_reveng_notes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrenderg%2Fsupervision_reveng_notes/lists"}