├── .gitignore ├── LICENSE ├── README.md ├── kicad ├── C64_Pi_Interface-rescue.dcm ├── C64_Pi_Interface.kicad_pcb ├── C64_Pi_Interface.net ├── C64_Pi_Interface.pro ├── C64_Pi_Interface.sch ├── C64_Pi_Interface.xml ├── fp-lib-table ├── lib │ └── C64-Exp-Port.dcm ├── mod │ └── C64-Cart.kicad_mod └── sym-lib-table ├── pi ├── 6502.cpp ├── 6569.cpp ├── 6569.h ├── 6581.cpp ├── 6581.h ├── Makefile ├── c64_pi_dump.tar.xz ├── char.rom ├── display.cpp ├── display.h ├── fake_pi.cpp ├── main.cpp ├── pi.cpp ├── pi.h └── sound.cpp ├── pics ├── board.jpg ├── data_transfer.jpg └── in_action.jpg └── vhdl ├── Makefile ├── README.txt ├── cpi.prj ├── cpi.xst ├── impact.batch ├── src ├── cpi.ucf ├── cpi.vhdl ├── exp_bus_ctrl.vhdl └── test_bench.vhd ├── test_bench.cmd ├── test_bench.prj └── test_bench.wcfg /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Kim Jørgensen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C64 - Pi Interface 2 | 3 | The C64 – Pi Interface gives you the capability to connect your Commodore 64 to an HDMI monitor, using a Raspberry Pi. This project is experimental, and not intended to be used as an end-user product. 4 | 5 | ![The board](pics/board.jpg) 6 | 7 | This project is inspired by the [RasPIC64 project](https://github.com/frntc/RasPIC64) but with different goals in mind: 8 | 9 | - Focus on both video and audio 10 | - Eliminate the need for any addition connections to the C64 besides the Expansion Port 11 | - Use the Pi’s SMI bus and DMA for efficient data transfer 12 | - Support Raspbian, no bare metal OS 13 | 14 | ... and the result: 15 | 16 | ![C64 – Pi Interface in action](pics/in_action.jpg) 17 | 18 | ## Video and Audio 19 | A modified version of [Frodo V4.1b](https://frodo.cebix.net/) is used for VIC-II and SID emulation running on the Pi. Frodo was chosen for its simplicity but the emulation quality is not as good as VICE. 20 | 21 | ## Expansion Port Signals Only 22 | The challenge of making a VIC-II and SID emulator using the Expansion Port is similar to the challenge of making a [Kernal Cartridge](https://github.com/KimJorgensen/easyflash/releases/download/EF3-FC3/kernal-cartridge.pdf), mainly that data written to the I/O register $01 of the 6510 CPU is not visible on the data bus. 23 | 24 | The following options were considered to solve this challenge: 25 | 1. Cheat and require an additional wire from the cartridge to be connected to the motherboard of the C64. This method is used by RasPIC64 26 | 2. Manipulate the Expansion Port signals in such way that the value of register $01 can be detected from the cartridge. This method is used by EasyFlash3 (for replacing the kernal). 27 | 3. Pause the CPU of the C64 and emulate it on the cartridge (or Pi). This method is used by the [Chameleon](http://wiki.icomp.de/wiki/Chameleon) 28 | 4. Emulate the CPU on the Pi in parallel with the one in the C64. This was the chosen method 29 | 30 | Option 2 was not chosen as it seems only to work for detecting the kernal mapping, not the I/O area. Option 4 does work, but has some drawbacks: 31 | - The Cassette Connector which is connected to I/O port of the 6510 CPU cannot be used or the emulator will get out of sync with the C64 32 | - The CPU emulator must be very accurate down to the bus level. Currently this is not 100% and not all opcodes are implemented 33 | - It requires additional resources to emulate the CPU on the Pi 34 | - This method requires a continuous stream of data from the C64. If any data is skipped the emulator will get out of sync 35 | 36 | ## Data transfer 37 | The Raspberry Pi’s SMI (Secondary Memory Interface) bus seems ideal for efficient data transfer of the C64’s Expansion Port signals to the Pi. However, even though a Linux kernel driver exists there is no official documentation for the SMI bus available, which led to a lot of trial and error. 38 | 39 | The Linux kernel driver is not used, as it is buggy in some kernel versions and a user space driver gives more control and makes debugging easier. But this makes implementation less efficient as the emulator employs polling to detect when a DMA transfer is complete. 40 | 41 | The CPLD firmware on the interface board includes only one register for storing the Expansion Port signals, thus requiring the Pi to read it’s content within each 6510 CPU cycle. Due to the latency involved when configuring the SMI and DMA registers, it is quite tricky to ensure a continuous stream of data without skipping any cycles. 42 | 43 | ![C64 – Pi data transfer](pics/data_transfer.jpg) 44 | 45 | One limitation of using the SMI bus and DMA is the data latency introduced by the FIFOs, which prevents emulation of most C64 cartridges. 46 | 47 | ## Raspbian 48 | Frodo was modified to use SDL2 to allow the use of hardware acceleration to filter and scale the image to full screen. However, the default SDL2 installation on Raspbian from the Raspberry Pi Foundation does not support hardware acceleration. Instead RetroPie is used which comes with a decent SDL2 installation out of the box. 49 | 50 | ## Results 51 | It works, but with the limitations described on this page. The video lag when using HDMI seems very low, but no actual test has been made. 52 | 53 | The current implementation is quite resource intensive and it barely runs stable on a Raspberry Pi 3 Model B+. Any "interruptions" like connecting to the Pi via SSH will cause the emulator to exit due to not being able to [keep up](https://www.youtube.com/watch?v=cuA8l5gLy7Q) with the data stream from the C64. 54 | It is speculated that moving to bare metal, like RasPIC64, would improve this. Also, an introduction of a FIFO on the interface board would relax the timing requirements a bit. 55 | 56 | During start-up of the C64, the emulator synchronizes its VIC-II with the one in the C64 using the BA signal. The current implementation is not perfect and sometimes fails, causing graphical glitches in games. 57 | 58 | The reset button on the interface board is not useful at the moment, as a power cycle is required to get the C64 synchronized with the emulator. 59 | 60 | So what’s the deal with the SPI flash and RAM on the interface board? 61 | That’s for an experiment another day and it is not certain if this ever will be useful. The footprint for the flash is also of the wrong size. Not being a hardware designer there are likely other mistakes made. 62 | -------------------------------------------------------------------------------- /kicad/C64_Pi_Interface-rescue.dcm: -------------------------------------------------------------------------------- 1 | EESchema-DOCLIB Version 2.0 2 | # 3 | #End Doc Library 4 | -------------------------------------------------------------------------------- /kicad/C64_Pi_Interface.pro: -------------------------------------------------------------------------------- 1 | update=22/05/2015 07:44:53 2 | version=1 3 | last_client=kicad 4 | [general] 5 | version=1 6 | RootSch= 7 | BoardNm= 8 | [pcbnew] 9 | version=1 10 | LastNetListRead= 11 | UseCmpFile=1 12 | PadDrill=0.600000000000 13 | PadDrillOvalY=0.600000000000 14 | PadSizeH=1.500000000000 15 | PadSizeV=1.500000000000 16 | PcbTextSizeV=1.500000000000 17 | PcbTextSizeH=1.500000000000 18 | PcbTextThickness=0.300000000000 19 | ModuleTextSizeV=1.000000000000 20 | ModuleTextSizeH=1.000000000000 21 | ModuleTextSizeThickness=0.150000000000 22 | SolderMaskClearance=0.000000000000 23 | SolderMaskMinWidth=0.000000000000 24 | DrawSegmentWidth=0.200000000000 25 | BoardOutlineThickness=0.100000000000 26 | ModuleOutlineThickness=0.150000000000 27 | [cvpcb] 28 | version=1 29 | NetIExt=net 30 | [eeschema] 31 | version=1 32 | LibDir= 33 | [eeschema/libraries] 34 | -------------------------------------------------------------------------------- /kicad/fp-lib-table: -------------------------------------------------------------------------------- 1 | (fp_lib_table 2 | (lib (name C64)(type KiCad)(uri ${KIPRJMOD}/mod)(options "")(descr "")) 3 | ) 4 | -------------------------------------------------------------------------------- /kicad/lib/C64-Exp-Port.dcm: -------------------------------------------------------------------------------- 1 | EESchema-DOCLIB Version 2.0 2 | # 3 | #End Doc Library 4 | -------------------------------------------------------------------------------- /kicad/mod/C64-Cart.kicad_mod: -------------------------------------------------------------------------------- 1 | (module C64-Cart (layer F.Cu) (tedit 5A47AD92) 2 | (fp_text reference U1 (at 3.81 -9.018274) (layer F.SilkS) hide 3 | (effects (font (size 1 1) (thickness 0.15))) 4 | ) 5 | (fp_text value C64-Exp-Port (at 30.48 1.27) (layer F.Fab) hide 6 | (effects (font (size 1 1) (thickness 0.15))) 7 | ) 8 | (fp_line (start 58.42 -35.56) (end 0 -35.56) (layer Dwgs.User) (width 0.15)) 9 | (fp_text user "Card will be inside C64 up to this point" (at 29.21 -36.576) (layer Dwgs.User) 10 | (effects (font (size 1 1) (thickness 0.15))) 11 | ) 12 | (fp_line (start 58.42 0) (end 58.42 -35.56) (layer Dwgs.User) (width 0.15)) 13 | (fp_line (start 0 0) (end 58.42 0) (layer F.Fab) (width 0.15)) 14 | (fp_line (start 0 -35.56) (end 0 0) (layer Dwgs.User) (width 0.15)) 15 | (pad 1 smd rect (at 2.54 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 16 | (pad 4 smd rect (at 10.16 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 17 | (pad 5 smd rect (at 12.7 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 18 | (pad 6 smd rect (at 15.24 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 19 | (pad 7 smd rect (at 17.78 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 20 | (pad 8 smd rect (at 20.32 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 21 | (pad 9 smd rect (at 22.86 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 22 | (pad 10 smd rect (at 25.4 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 23 | (pad 11 smd rect (at 27.94 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 24 | (pad 13 smd rect (at 33.02 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 25 | (pad 14 smd rect (at 35.56 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 26 | (pad 12 smd rect (at 30.48 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 27 | (pad 2 smd rect (at 5.08 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 28 | (pad 3 smd rect (at 7.62 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 29 | (pad 15 smd rect (at 38.1 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 30 | (pad 16 smd rect (at 40.64 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 31 | (pad 17 smd rect (at 43.18 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 32 | (pad 18 smd rect (at 45.72 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 33 | (pad 19 smd rect (at 48.26 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 34 | (pad 20 smd rect (at 50.8 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 35 | (pad 21 smd rect (at 53.34 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 36 | (pad 22 smd rect (at 55.88 -3.938274 180) (size 1.524 8) (layers F.Cu F.Paste F.Mask)) 37 | (pad F smd rect (at 15.24 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 38 | (pad H smd rect (at 17.78 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 39 | (pad J smd rect (at 20.32 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 40 | (pad K smd rect (at 22.86 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 41 | (pad L smd rect (at 25.4 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 42 | (pad M smd rect (at 27.94 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 43 | (pad B smd rect (at 5.08 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 44 | (pad C smd rect (at 7.62 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 45 | (pad D smd rect (at 10.16 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 46 | (pad E smd rect (at 12.7 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 47 | (pad A smd rect (at 2.54 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 48 | (pad U smd rect (at 43.18 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 49 | (pad V smd rect (at 45.72 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 50 | (pad W smd rect (at 48.26 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 51 | (pad X smd rect (at 50.8 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 52 | (pad Y smd rect (at 53.34 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 53 | (pad Z smd rect (at 55.88 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 54 | (pad R smd rect (at 35.56 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 55 | (pad S smd rect (at 38.1 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 56 | (pad T smd rect (at 40.64 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 57 | (pad P smd rect (at 33.02 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 58 | (pad N smd rect (at 30.48 -3.938274) (size 1.524 8) (layers B.Cu B.Paste B.Mask)) 59 | (pad NoSo smd rect (at 29.21 -3.81 180) (size 58.42 8.3) (layers B.Mask)) 60 | (pad NoSo smd rect (at 29.21 -3.81 180) (size 58.42 8.3) (layers F.Mask)) 61 | ) 62 | -------------------------------------------------------------------------------- /kicad/sym-lib-table: -------------------------------------------------------------------------------- 1 | (sym_lib_table 2 | (lib (name C64-Exp-Port)(type Legacy)(uri "/home/kim/Hardware/Raspberry Pi/C64_interface/C64_Pi_Interface/lib/C64-Exp-Port.lib")(options "")(descr "")) 3 | (lib (name C64_Pi_Interface-rescue)(type Legacy)(uri ${KIPRJMOD}/C64_Pi_Interface-rescue.lib)(options "")(descr "")) 4 | ) 5 | -------------------------------------------------------------------------------- /pi/6569.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Based on VIC_SC.cpp - 6569R5 emulation (cycle based) 3 | * Frodo (C) 1994-1997,2002 Christian Bauer 4 | * 5 | * Incompatibilities: 6 | * ------------------ 7 | * 8 | * - Color of $ff bytes read when BA is low and AEC is high 9 | * is not correct 10 | * - Changes to border/background color are visible 7 pixels 11 | * too late 12 | * - Sprite data access doesn't respect BA 13 | * - Sprite collisions are only detected within the visible 14 | * screen area (excluding borders) 15 | * - Sprites are only drawn if they completely fit within the 16 | * left/right limits of the chunky bitmap 17 | */ 18 | 19 | #include "6569.h" 20 | 21 | static const bool SpritesOn = true; // Sprite display is on 22 | static const bool SpriteCollisions = false; // Sprite collision detection is off 23 | 24 | // First and last displayed line 25 | static const int FIRST_DISP_LINE = 0x10; 26 | static const int LAST_DISP_LINE = 0x11f; 27 | 28 | // First and last possible line for Bad Lines 29 | static const int FIRST_DMA_LINE = 0x30; 30 | static const int LAST_DMA_LINE = 0xf7; 31 | 32 | // Display window coordinates 33 | static const int ROW25_YSTART = 0x33; 34 | static const int ROW25_YSTOP = 0xfb; 35 | static const int ROW24_YSTART = 0x37; 36 | static const int ROW24_YSTOP = 0xf7; 37 | 38 | static const int COL40_XSTART = 0x20; 39 | static const int COL40_XSTOP = 0x160; 40 | static const int COL38_XSTART = 0x27; 41 | static const int COL38_XSTOP = 0x157; 42 | 43 | 44 | // Tables for sprite X expansion 45 | static const uint16_t ExpTable[256] = { 46 | 0x0000, 0x0003, 0x000C, 0x000F, 0x0030, 0x0033, 0x003C, 0x003F, 47 | 0x00C0, 0x00C3, 0x00CC, 0x00CF, 0x00F0, 0x00F3, 0x00FC, 0x00FF, 48 | 0x0300, 0x0303, 0x030C, 0x030F, 0x0330, 0x0333, 0x033C, 0x033F, 49 | 0x03C0, 0x03C3, 0x03CC, 0x03CF, 0x03F0, 0x03F3, 0x03FC, 0x03FF, 50 | 0x0C00, 0x0C03, 0x0C0C, 0x0C0F, 0x0C30, 0x0C33, 0x0C3C, 0x0C3F, 51 | 0x0CC0, 0x0CC3, 0x0CCC, 0x0CCF, 0x0CF0, 0x0CF3, 0x0CFC, 0x0CFF, 52 | 0x0F00, 0x0F03, 0x0F0C, 0x0F0F, 0x0F30, 0x0F33, 0x0F3C, 0x0F3F, 53 | 0x0FC0, 0x0FC3, 0x0FCC, 0x0FCF, 0x0FF0, 0x0FF3, 0x0FFC, 0x0FFF, 54 | 0x3000, 0x3003, 0x300C, 0x300F, 0x3030, 0x3033, 0x303C, 0x303F, 55 | 0x30C0, 0x30C3, 0x30CC, 0x30CF, 0x30F0, 0x30F3, 0x30FC, 0x30FF, 56 | 0x3300, 0x3303, 0x330C, 0x330F, 0x3330, 0x3333, 0x333C, 0x333F, 57 | 0x33C0, 0x33C3, 0x33CC, 0x33CF, 0x33F0, 0x33F3, 0x33FC, 0x33FF, 58 | 0x3C00, 0x3C03, 0x3C0C, 0x3C0F, 0x3C30, 0x3C33, 0x3C3C, 0x3C3F, 59 | 0x3CC0, 0x3CC3, 0x3CCC, 0x3CCF, 0x3CF0, 0x3CF3, 0x3CFC, 0x3CFF, 60 | 0x3F00, 0x3F03, 0x3F0C, 0x3F0F, 0x3F30, 0x3F33, 0x3F3C, 0x3F3F, 61 | 0x3FC0, 0x3FC3, 0x3FCC, 0x3FCF, 0x3FF0, 0x3FF3, 0x3FFC, 0x3FFF, 62 | 0xC000, 0xC003, 0xC00C, 0xC00F, 0xC030, 0xC033, 0xC03C, 0xC03F, 63 | 0xC0C0, 0xC0C3, 0xC0CC, 0xC0CF, 0xC0F0, 0xC0F3, 0xC0FC, 0xC0FF, 64 | 0xC300, 0xC303, 0xC30C, 0xC30F, 0xC330, 0xC333, 0xC33C, 0xC33F, 65 | 0xC3C0, 0xC3C3, 0xC3CC, 0xC3CF, 0xC3F0, 0xC3F3, 0xC3FC, 0xC3FF, 66 | 0xCC00, 0xCC03, 0xCC0C, 0xCC0F, 0xCC30, 0xCC33, 0xCC3C, 0xCC3F, 67 | 0xCCC0, 0xCCC3, 0xCCCC, 0xCCCF, 0xCCF0, 0xCCF3, 0xCCFC, 0xCCFF, 68 | 0xCF00, 0xCF03, 0xCF0C, 0xCF0F, 0xCF30, 0xCF33, 0xCF3C, 0xCF3F, 69 | 0xCFC0, 0xCFC3, 0xCFCC, 0xCFCF, 0xCFF0, 0xCFF3, 0xCFFC, 0xCFFF, 70 | 0xF000, 0xF003, 0xF00C, 0xF00F, 0xF030, 0xF033, 0xF03C, 0xF03F, 71 | 0xF0C0, 0xF0C3, 0xF0CC, 0xF0CF, 0xF0F0, 0xF0F3, 0xF0FC, 0xF0FF, 72 | 0xF300, 0xF303, 0xF30C, 0xF30F, 0xF330, 0xF333, 0xF33C, 0xF33F, 73 | 0xF3C0, 0xF3C3, 0xF3CC, 0xF3CF, 0xF3F0, 0xF3F3, 0xF3FC, 0xF3FF, 74 | 0xFC00, 0xFC03, 0xFC0C, 0xFC0F, 0xFC30, 0xFC33, 0xFC3C, 0xFC3F, 75 | 0xFCC0, 0xFCC3, 0xFCCC, 0xFCCF, 0xFCF0, 0xFCF3, 0xFCFC, 0xFCFF, 76 | 0xFF00, 0xFF03, 0xFF0C, 0xFF0F, 0xFF30, 0xFF33, 0xFF3C, 0xFF3F, 77 | 0xFFC0, 0xFFC3, 0xFFCC, 0xFFCF, 0xFFF0, 0xFFF3, 0xFFFC, 0xFFFF 78 | }; 79 | 80 | static const uint16_t MultiExpTable[256] = { 81 | 0x0000, 0x0005, 0x000A, 0x000F, 0x0050, 0x0055, 0x005A, 0x005F, 82 | 0x00A0, 0x00A5, 0x00AA, 0x00AF, 0x00F0, 0x00F5, 0x00FA, 0x00FF, 83 | 0x0500, 0x0505, 0x050A, 0x050F, 0x0550, 0x0555, 0x055A, 0x055F, 84 | 0x05A0, 0x05A5, 0x05AA, 0x05AF, 0x05F0, 0x05F5, 0x05FA, 0x05FF, 85 | 0x0A00, 0x0A05, 0x0A0A, 0x0A0F, 0x0A50, 0x0A55, 0x0A5A, 0x0A5F, 86 | 0x0AA0, 0x0AA5, 0x0AAA, 0x0AAF, 0x0AF0, 0x0AF5, 0x0AFA, 0x0AFF, 87 | 0x0F00, 0x0F05, 0x0F0A, 0x0F0F, 0x0F50, 0x0F55, 0x0F5A, 0x0F5F, 88 | 0x0FA0, 0x0FA5, 0x0FAA, 0x0FAF, 0x0FF0, 0x0FF5, 0x0FFA, 0x0FFF, 89 | 0x5000, 0x5005, 0x500A, 0x500F, 0x5050, 0x5055, 0x505A, 0x505F, 90 | 0x50A0, 0x50A5, 0x50AA, 0x50AF, 0x50F0, 0x50F5, 0x50FA, 0x50FF, 91 | 0x5500, 0x5505, 0x550A, 0x550F, 0x5550, 0x5555, 0x555A, 0x555F, 92 | 0x55A0, 0x55A5, 0x55AA, 0x55AF, 0x55F0, 0x55F5, 0x55FA, 0x55FF, 93 | 0x5A00, 0x5A05, 0x5A0A, 0x5A0F, 0x5A50, 0x5A55, 0x5A5A, 0x5A5F, 94 | 0x5AA0, 0x5AA5, 0x5AAA, 0x5AAF, 0x5AF0, 0x5AF5, 0x5AFA, 0x5AFF, 95 | 0x5F00, 0x5F05, 0x5F0A, 0x5F0F, 0x5F50, 0x5F55, 0x5F5A, 0x5F5F, 96 | 0x5FA0, 0x5FA5, 0x5FAA, 0x5FAF, 0x5FF0, 0x5FF5, 0x5FFA, 0x5FFF, 97 | 0xA000, 0xA005, 0xA00A, 0xA00F, 0xA050, 0xA055, 0xA05A, 0xA05F, 98 | 0xA0A0, 0xA0A5, 0xA0AA, 0xA0AF, 0xA0F0, 0xA0F5, 0xA0FA, 0xA0FF, 99 | 0xA500, 0xA505, 0xA50A, 0xA50F, 0xA550, 0xA555, 0xA55A, 0xA55F, 100 | 0xA5A0, 0xA5A5, 0xA5AA, 0xA5AF, 0xA5F0, 0xA5F5, 0xA5FA, 0xA5FF, 101 | 0xAA00, 0xAA05, 0xAA0A, 0xAA0F, 0xAA50, 0xAA55, 0xAA5A, 0xAA5F, 102 | 0xAAA0, 0xAAA5, 0xAAAA, 0xAAAF, 0xAAF0, 0xAAF5, 0xAAFA, 0xAAFF, 103 | 0xAF00, 0xAF05, 0xAF0A, 0xAF0F, 0xAF50, 0xAF55, 0xAF5A, 0xAF5F, 104 | 0xAFA0, 0xAFA5, 0xAFAA, 0xAFAF, 0xAFF0, 0xAFF5, 0xAFFA, 0xAFFF, 105 | 0xF000, 0xF005, 0xF00A, 0xF00F, 0xF050, 0xF055, 0xF05A, 0xF05F, 106 | 0xF0A0, 0xF0A5, 0xF0AA, 0xF0AF, 0xF0F0, 0xF0F5, 0xF0FA, 0xF0FF, 107 | 0xF500, 0xF505, 0xF50A, 0xF50F, 0xF550, 0xF555, 0xF55A, 0xF55F, 108 | 0xF5A0, 0xF5A5, 0xF5AA, 0xF5AF, 0xF5F0, 0xF5F5, 0xF5FA, 0xF5FF, 109 | 0xFA00, 0xFA05, 0xFA0A, 0xFA0F, 0xFA50, 0xFA55, 0xFA5A, 0xFA5F, 110 | 0xFAA0, 0xFAA5, 0xFAAA, 0xFAAF, 0xFAF0, 0xFAF5, 0xFAFA, 0xFAFF, 111 | 0xFF00, 0xFF05, 0xFF0A, 0xFF0F, 0xFF50, 0xFF55, 0xFF5A, 0xFF5F, 112 | 0xFFA0, 0xFFA5, 0xFFAA, 0xFFAF, 0xFFF0, 0xFFF5, 0xFFFA, 0xFFFF 113 | }; 114 | 115 | static uint16_t mx[8]; // VIC registers 116 | static uint8_t my[8]; 117 | static uint8_t mx8; 118 | static uint8_t ctrl1, ctrl2; 119 | static uint8_t lpx, lpy; 120 | static uint8_t me, mxe, mye, mdp, mmc; 121 | static uint8_t vbase; 122 | static uint8_t irq_flag, irq_mask; 123 | static uint8_t clx_spr, clx_bgr; 124 | static uint8_t sc[8]; 125 | 126 | static uint32_t colors[256]; // The 16 C64 colors (16 times mirrored to avoid "& 0x0f") 127 | 128 | static uint32_t ec_color, b0c_color, b1c_color, b2c_color, b3c_color; // Exterior/background colors 129 | static uint32_t mm0_color, mm1_color; // MOB multicolors 130 | static uint32_t spr_color[8]; // MOB colors 131 | 132 | static uint8_t matrix_line[40]; // Buffer for video line, read in Bad Lines 133 | static uint8_t color_line[40]; // Buffer for color line, read in Bad Lines 134 | 135 | static uint32_t *chunky_ptr; // Pointer in chunky bitmap buffer 136 | static uint32_t *chunky_line_start; // Pointer to start of current line in bitmap buffer 137 | static uint8_t *fore_mask_ptr; // Pointer in fore_mask_buf 138 | static int xmod; // Number of bytes per row 139 | 140 | static uint16_t raster_x; // Current raster x position 141 | static uint16_t raster_y; // Current raster line 142 | static uint16_t irq_raster; // Interrupt raster line 143 | static uint16_t dy_start; // Comparison values for border logic 144 | static uint16_t dy_stop; 145 | static uint16_t rc; // Row counter 146 | static uint16_t vc; // Video counter 147 | static uint16_t vc_base; // Video counter base 148 | static uint16_t x_scroll; // X scroll value 149 | static uint16_t y_scroll; // Y scroll value 150 | static uint16_t cia_vabase; // CIA VA14/15 video base 151 | 152 | static int cycle; // Current cycle in line (1..63) 153 | 154 | static int display_idx; // Index of current display mode 155 | static int ml_index; // Index in matrix/color_line[] 156 | 157 | static uint16_t mc[8]; // Sprite data counters 158 | static uint16_t mc_base[8]; // Sprite data counter bases 159 | 160 | static uint8_t spr_coll_buf[0x180]; // Buffer for sprite-sprite collisions and priorities 161 | static uint8_t fore_mask_buf[0x180/8]; // Foreground mask for sprite-graphics collisions and priorities 162 | 163 | static bool display_state; // true: Display state, false: Idle state 164 | static bool border_on; // Flag: Upper/lower border on 165 | static bool bad_lines_enabled; // Flag: Bad Lines enabled for this frame 166 | static bool lp_triggered; // Flag: Lightpen was triggered in this frame 167 | static bool is_bad_line; // Flag: Current line is Bad Line 168 | static bool draw_this_line; // Flag: This line is drawn on the screen 169 | static bool ud_border_on; // Flag: Upper/lower border on 170 | static bool vblanking; // Flag: VBlank in next cycle 171 | 172 | static bool border_on_sample[5]; // Samples of border state at different cycles (1, 17, 18, 56, 57) 173 | static uint32_t border_color_sample[DISPLAY_X/8]; // Samples of border color at each "displayed" cycle 174 | 175 | static uint16_t matrix_base; // Video matrix base 176 | static uint16_t char_base; // Character generator base 177 | static uint16_t bitmap_base; // Bitmap base 178 | 179 | static uint8_t ref_cnt; // Refresh counter 180 | static uint8_t spr_exp_y; // 8 sprite y expansion flipflops 181 | static uint8_t spr_dma_on; // 8 flags: Sprite DMA active 182 | static uint8_t spr_disp_on; // 8 flags: Sprite display active 183 | static uint8_t spr_draw; // 8 flags: Draw sprite in this line 184 | static uint16_t spr_ptr[8]; // Sprite data pointers 185 | 186 | static uint8_t gfx_data, char_data, color_data, last_char_data; 187 | static uint8_t spr_data[8][4]; // Sprite data read 188 | static uint8_t spr_draw_data[8][4]; // Sprite data for drawing 189 | 190 | static uint32_t first_ba_cycle; // Cycle when BA first went low 191 | static uint8_t LastVICByte; 192 | 193 | /* 194 | * Initialize variables 195 | */ 196 | 197 | static void reset6569() 198 | { 199 | int i; 200 | 201 | // Set pointers 202 | matrix_base = 0; 203 | char_base = 0; 204 | bitmap_base = 0; 205 | 206 | // Get bitmap info 207 | chunky_ptr = chunky_line_start = display_bitmap_base; 208 | xmod = display_bitmap_xmod; 209 | 210 | // Initialize VIC registers 211 | mx8 = 0; 212 | ctrl1 = ctrl2 = 0; 213 | lpx = lpy = 0; 214 | me = mxe = mye = mdp = mmc = 0; 215 | vbase = irq_flag = irq_mask = 0; 216 | clx_spr = clx_bgr = 0; 217 | cia_vabase = 0; 218 | for (i=0; i<8; i++) mx[i] = my[i] = sc[i] = 0; 219 | 220 | // Initialize other variables 221 | raster_y = TOTAL_RASTERS - 1; 222 | rc = 7; 223 | irq_raster = vc = vc_base = x_scroll = y_scroll = 0; 224 | dy_start = ROW24_YSTART; 225 | dy_stop = ROW24_YSTOP; 226 | ml_index = 0; 227 | 228 | cycle = 1; 229 | display_idx = 0; 230 | display_state = false; 231 | border_on = ud_border_on = vblanking = false; 232 | lp_triggered = draw_this_line = false; 233 | 234 | spr_dma_on = spr_disp_on = 0; 235 | for (i=0; i<8; i++) { 236 | mc[i] = 63; 237 | spr_ptr[i] = 0; 238 | } 239 | 240 | memset(spr_coll_buf, 0, 0x180); 241 | memset(fore_mask_buf, 0, 0x180/8); 242 | 243 | for (int i=0; i<256; i++) 244 | { 245 | colors[i] = palette[i & 0x0f]; 246 | } 247 | 248 | // Preset colors to black 249 | ec_color = b0c_color = b1c_color = b2c_color = b3c_color = mm0_color = mm1_color = colors[0]; 250 | for (i=0; i<8; i++) 251 | { 252 | spr_color[i] = colors[0]; 253 | } 254 | } 255 | 256 | 257 | /* 258 | * Trigger raster IRQ 259 | */ 260 | 261 | static inline void raster_irq(void) 262 | { 263 | irq_flag |= 0x01; 264 | if (irq_mask & 0x01) { 265 | irq_flag |= 0x80; 266 | vic_trigger_irq(); 267 | } 268 | } 269 | 270 | 271 | /* 272 | * Write to VIC register 273 | */ 274 | 275 | static void write_register_6569(uint16_t adr, uint8_t byte) 276 | { 277 | switch (adr) { 278 | case 0x00: case 0x02: case 0x04: case 0x06: 279 | case 0x08: case 0x0a: case 0x0c: case 0x0e: 280 | mx[adr >> 1] = (mx[adr >> 1] & 0xff00) | byte; 281 | break; 282 | 283 | case 0x10:{ 284 | int i, j; 285 | mx8 = byte; 286 | for (i=0, j=1; i<8; i++, j<<=1) { 287 | if (mx8 & j) 288 | mx[i] |= 0x100; 289 | else 290 | mx[i] &= 0xff; 291 | } 292 | break; 293 | } 294 | 295 | case 0x01: case 0x03: case 0x05: case 0x07: 296 | case 0x09: case 0x0b: case 0x0d: case 0x0f: 297 | my[adr >> 1] = byte; 298 | break; 299 | 300 | case 0x11:{ // Control register 1 301 | ctrl1 = byte; 302 | y_scroll = byte & 7; 303 | 304 | uint16_t new_irq_raster = (irq_raster & 0xff) | ((byte & 0x80) << 1); 305 | if (irq_raster != new_irq_raster && raster_y == new_irq_raster) 306 | raster_irq(); 307 | irq_raster = new_irq_raster; 308 | 309 | if (byte & 8) { 310 | dy_start = ROW25_YSTART; 311 | dy_stop = ROW25_YSTOP; 312 | } else { 313 | dy_start = ROW24_YSTART; 314 | dy_stop = ROW24_YSTOP; 315 | } 316 | 317 | // In line $30, the DEN bit controls if Bad Lines can occur 318 | if (raster_y == 0x30 && byte & 0x10) 319 | bad_lines_enabled = true; 320 | 321 | // Bad Line condition? 322 | is_bad_line = (raster_y >= FIRST_DMA_LINE && raster_y <= LAST_DMA_LINE && ((raster_y & 7) == y_scroll) && bad_lines_enabled); 323 | 324 | display_idx = ((ctrl1 & 0x60) | (ctrl2 & 0x10)) >> 4; 325 | break; 326 | } 327 | 328 | case 0x12:{ // Raster counter 329 | uint16_t new_irq_raster = (irq_raster & 0xff00) | byte; 330 | if (irq_raster != new_irq_raster && raster_y == new_irq_raster) 331 | raster_irq(); 332 | irq_raster = new_irq_raster; 333 | break; 334 | } 335 | 336 | case 0x15: // Sprite enable 337 | me = byte; 338 | break; 339 | 340 | case 0x16: // Control register 2 341 | ctrl2 = byte; 342 | x_scroll = byte & 7; 343 | display_idx = ((ctrl1 & 0x60) | (ctrl2 & 0x10)) >> 4; 344 | break; 345 | 346 | case 0x17: // Sprite Y expansion 347 | mye = byte; 348 | spr_exp_y |= ~byte; 349 | break; 350 | 351 | case 0x18: // Memory pointers 352 | vbase = byte; 353 | matrix_base = (byte & 0xf0) << 6; 354 | char_base = (byte & 0x0e) << 10; 355 | bitmap_base = (byte & 0x08) << 10; 356 | break; 357 | 358 | case 0x19: // IRQ flags 359 | irq_flag = irq_flag & (~byte & 0x0f); 360 | if (irq_flag & irq_mask) // Set master bit if allowed interrupt still pending 361 | irq_flag |= 0x80; 362 | else 363 | vic_clear_irq(); // Else clear interrupt 364 | break; 365 | 366 | case 0x1a: // IRQ mask 367 | irq_mask = byte & 0x0f; 368 | if (irq_flag & irq_mask) { // Trigger interrupt if pending and now allowed 369 | irq_flag |= 0x80; 370 | vic_trigger_irq(); 371 | } else { 372 | irq_flag &= 0x7f; 373 | vic_clear_irq(); 374 | } 375 | break; 376 | 377 | case 0x1b: // Sprite data priority 378 | mdp = byte; 379 | break; 380 | 381 | case 0x1c: // Sprite multicolor 382 | mmc = byte; 383 | break; 384 | 385 | case 0x1d: // Sprite X expansion 386 | mxe = byte; 387 | break; 388 | 389 | case 0x20: ec_color = colors[byte]; break; 390 | case 0x21: b0c_color = colors[byte]; break; 391 | case 0x22: b1c_color = colors[byte]; break; 392 | case 0x23: b2c_color = colors[byte]; break; 393 | case 0x24: b3c_color = colors[byte]; break; 394 | case 0x25: mm0_color = colors[byte]; break; 395 | case 0x26: mm1_color = colors[byte]; break; 396 | 397 | case 0x27: case 0x28: case 0x29: case 0x2a: 398 | case 0x2b: case 0x2c: case 0x2d: case 0x2e: 399 | spr_color[adr - 0x27] = colors[sc[adr - 0x27] = byte]; 400 | break; 401 | } 402 | } 403 | 404 | 405 | /* 406 | * CIA VA14/15 has changed 407 | */ 408 | 409 | static void changed_va_6569(uint16_t new_va) 410 | { 411 | cia_vabase = new_va << 14; 412 | write_register_6569(0x18, vbase); // Force update of memory pointers 413 | } 414 | 415 | 416 | /* 417 | * Read a byte from the VIC's address space 418 | */ 419 | 420 | inline static uint8_t read_byte(uint16_t adr) 421 | { 422 | uint16_t va = adr | cia_vabase; 423 | 424 | if ((va & 0x7000) == 0x1000) 425 | return LastVICByte = char_rom[va & 0x0fff]; 426 | else 427 | return LastVICByte = ram[va]; 428 | } 429 | 430 | 431 | /* 432 | * Quick memset of 8 bytes 433 | */ 434 | 435 | inline static void memset8(uint32_t *p, uint32_t c) 436 | { 437 | p[0] = p[1] = p[2] = p[3] = p[4] = p[5] = p[6] = p[7] = c; 438 | } 439 | 440 | 441 | /* 442 | * Video matrix access 443 | */ 444 | 445 | inline static void matrix_access(void) 446 | { 447 | if (vic_ba_Low) { 448 | if (cycle_counter-first_ba_cycle < 3) 449 | matrix_line[ml_index] = color_line[ml_index] = 0xff; 450 | else { 451 | uint16_t adr = (vc & 0x03ff) | matrix_base; 452 | matrix_line[ml_index] = read_byte(adr); 453 | color_line[ml_index] = color_ram[adr & 0x03ff]; 454 | } 455 | } 456 | } 457 | 458 | 459 | /* 460 | * Graphics data access 461 | */ 462 | 463 | inline static void graphics_access(void) 464 | { 465 | if (display_state) { 466 | 467 | uint16_t adr; 468 | if (ctrl1 & 0x20) // Bitmap 469 | adr = ((vc & 0x03ff) << 3) | bitmap_base | rc; 470 | else // Text 471 | adr = (matrix_line[ml_index] << 3) | char_base | rc; 472 | if (ctrl1 & 0x40) // ECM 473 | adr &= 0xf9ff; 474 | gfx_data = read_byte(adr); 475 | char_data = matrix_line[ml_index]; 476 | color_data = color_line[ml_index]; 477 | ml_index++; 478 | vc++; 479 | 480 | } else { 481 | 482 | // Display is off 483 | gfx_data = read_byte(ctrl1 & 0x40 ? 0x39ff : 0x3fff); 484 | char_data = color_data = 0; 485 | } 486 | } 487 | 488 | 489 | /* 490 | * Background display (8 pixels) 491 | */ 492 | 493 | static void draw_background(void) 494 | { 495 | uint32_t *p = chunky_ptr; 496 | uint32_t c; 497 | 498 | if (!draw_this_line) 499 | return; 500 | 501 | switch (display_idx) { 502 | case 0: // Standard text 503 | case 1: // Multicolor text 504 | case 3: // Multicolor bitmap 505 | c = b0c_color; 506 | break; 507 | case 2: // Standard bitmap 508 | c = colors[last_char_data]; 509 | break; 510 | case 4: // ECM text 511 | if (last_char_data & 0x80) 512 | if (last_char_data & 0x40) 513 | c = b3c_color; 514 | else 515 | c = b2c_color; 516 | else 517 | if (last_char_data & 0x40) 518 | c = b1c_color; 519 | else 520 | c = b0c_color; 521 | break; 522 | default: 523 | c = colors[0]; 524 | break; 525 | } 526 | memset8(p, c); 527 | } 528 | 529 | 530 | /* 531 | * Graphics display (8 pixels) 532 | */ 533 | 534 | inline static void draw_graphics(void) 535 | { 536 | uint32_t *p = chunky_ptr + x_scroll; 537 | uint32_t c[4], data; 538 | 539 | if (!draw_this_line) 540 | return; 541 | if (ud_border_on) { 542 | draw_background(); 543 | return; 544 | } 545 | 546 | switch (display_idx) { 547 | 548 | case 0: // Standard text 549 | c[0] = b0c_color; 550 | c[1] = colors[color_data]; 551 | goto draw_std; 552 | 553 | case 1: // Multicolor text 554 | if (color_data & 8) { 555 | c[0] = b0c_color; 556 | c[1] = b1c_color; 557 | c[2] = b2c_color; 558 | c[3] = colors[color_data & 7]; 559 | goto draw_multi; 560 | } else { 561 | c[0] = b0c_color; 562 | c[1] = colors[color_data]; 563 | goto draw_std; 564 | } 565 | 566 | case 2: // Standard bitmap 567 | c[0] = colors[char_data]; 568 | c[1] = colors[char_data >> 4]; 569 | goto draw_std; 570 | 571 | case 3: // Multicolor bitmap 572 | c[0]= b0c_color; 573 | c[1] = colors[char_data >> 4]; 574 | c[2] = colors[char_data]; 575 | c[3] = colors[color_data]; 576 | goto draw_multi; 577 | 578 | case 4: // ECM text 579 | if (char_data & 0x80) 580 | if (char_data & 0x40) 581 | c[0] = b3c_color; 582 | else 583 | c[0] = b2c_color; 584 | else 585 | if (char_data & 0x40) 586 | c[0] = b1c_color; 587 | else 588 | c[0] = b0c_color; 589 | c[1] = colors[color_data]; 590 | goto draw_std; 591 | 592 | case 5: // Invalid multicolor text 593 | memset8(p, colors[0]); 594 | if (color_data & 8) { 595 | fore_mask_ptr[0] |= ((gfx_data & 0xaa) | (gfx_data & 0xaa) >> 1) >> x_scroll; 596 | fore_mask_ptr[1] |= ((gfx_data & 0xaa) | (gfx_data & 0xaa) >> 1) << (8-x_scroll); 597 | } else { 598 | fore_mask_ptr[0] |= gfx_data >> x_scroll; 599 | fore_mask_ptr[1] |= gfx_data << (7-x_scroll); 600 | } 601 | return; 602 | 603 | case 6: // Invalid standard bitmap 604 | memset8(p, colors[0]); 605 | fore_mask_ptr[0] |= gfx_data >> x_scroll; 606 | fore_mask_ptr[1] |= gfx_data << (7-x_scroll); 607 | return; 608 | 609 | case 7: // Invalid multicolor bitmap 610 | memset8(p, colors[0]); 611 | fore_mask_ptr[0] |= ((gfx_data & 0xaa) | (gfx_data & 0xaa) >> 1) >> x_scroll; 612 | fore_mask_ptr[1] |= ((gfx_data & 0xaa) | (gfx_data & 0xaa) >> 1) << (8-x_scroll); 613 | return; 614 | 615 | default: // Can't happen 616 | return; 617 | } 618 | 619 | draw_std: 620 | 621 | fore_mask_ptr[0] |= gfx_data >> x_scroll; 622 | fore_mask_ptr[1] |= gfx_data << (7-x_scroll); 623 | 624 | data = gfx_data; 625 | p[7] = c[data & 1]; data >>= 1; 626 | p[6] = c[data & 1]; data >>= 1; 627 | p[5] = c[data & 1]; data >>= 1; 628 | p[4] = c[data & 1]; data >>= 1; 629 | p[3] = c[data & 1]; data >>= 1; 630 | p[2] = c[data & 1]; data >>= 1; 631 | p[1] = c[data & 1]; data >>= 1; 632 | p[0] = c[data]; 633 | return; 634 | 635 | draw_multi: 636 | 637 | fore_mask_ptr[0] |= ((gfx_data & 0xaa) | (gfx_data & 0xaa) >> 1) >> x_scroll; 638 | fore_mask_ptr[1] |= ((gfx_data & 0xaa) | (gfx_data & 0xaa) >> 1) << (8-x_scroll); 639 | 640 | data = gfx_data; 641 | p[7] = p[6] = c[data & 3]; data >>= 2; 642 | p[5] = p[4] = c[data & 3]; data >>= 2; 643 | p[3] = p[2] = c[data & 3]; data >>= 2; 644 | p[1] = p[0] = c[data]; 645 | return; 646 | } 647 | 648 | 649 | /* 650 | * Sprite display 651 | */ 652 | 653 | inline static void draw_sprites(void) 654 | { 655 | int i; 656 | int snum, sbit; // Sprite number/bit mask 657 | int spr_coll=0, gfx_coll=0; 658 | 659 | // Clear sprite collision buffer 660 | { 661 | uint32_t *lp = (uint32_t *)spr_coll_buf - 1; 662 | for (i=0; i> (8-sshift)); 684 | 685 | if (mxe & sbit) { // X-expanded 686 | if (mx[snum] > DISPLAY_X-56) 687 | continue; 688 | 689 | uint32_t sdata_l = 0, sdata_r = 0, fore_mask_r; 690 | fore_mask_r = (((*(fmbp+4) << 24) | (*(fmbp+5) << 16) | (*(fmbp+6) << 8) 691 | | (*(fmbp+7))) << sshift) | (*(fmbp+8) >> (8-sshift)); 692 | 693 | if (mmc & sbit) { // Multicolor mode 694 | uint32_t plane0_l, plane0_r, plane1_l, plane1_r; 695 | 696 | // Expand sprite data 697 | sdata_l = MultiExpTable[sdata >> 24 & 0xff] << 16 | MultiExpTable[sdata >> 16 & 0xff]; 698 | sdata_r = MultiExpTable[sdata >> 8 & 0xff] << 16; 699 | 700 | // Convert sprite chunky pixels to bitplanes 701 | plane0_l = (sdata_l & 0x55555555) | (sdata_l & 0x55555555) << 1; 702 | plane1_l = (sdata_l & 0xaaaaaaaa) | (sdata_l & 0xaaaaaaaa) >> 1; 703 | plane0_r = (sdata_r & 0x55555555) | (sdata_r & 0x55555555) << 1; 704 | plane1_r = (sdata_r & 0xaaaaaaaa) | (sdata_r & 0xaaaaaaaa) >> 1; 705 | 706 | // Collision with graphics? 707 | if ((fore_mask & (plane0_l | plane1_l)) || (fore_mask_r & (plane0_r | plane1_r))) { 708 | gfx_coll |= sbit; 709 | if (mdp & sbit) { 710 | plane0_l &= ~fore_mask; // Mask sprite if in background 711 | plane1_l &= ~fore_mask; 712 | plane0_r &= ~fore_mask_r; 713 | plane1_r &= ~fore_mask_r; 714 | } 715 | } 716 | 717 | // Paint sprite 718 | for (i=0; i<32; i++, plane0_l<<=1, plane1_l<<=1) { 719 | uint32_t col; 720 | if (plane1_l & 0x80000000) { 721 | if (plane0_l & 0x80000000) 722 | col = mm1_color; 723 | else 724 | col = color; 725 | } else { 726 | if (plane0_l & 0x80000000) 727 | col = mm0_color; 728 | else 729 | continue; 730 | } 731 | if (q[i]) 732 | spr_coll |= q[i] | sbit; 733 | else { 734 | p[i] = col; 735 | q[i] = sbit; 736 | } 737 | } 738 | for (; i<48; i++, plane0_r<<=1, plane1_r<<=1) { 739 | uint32_t col; 740 | if (plane1_r & 0x80000000) { 741 | if (plane0_r & 0x80000000) 742 | col = mm1_color; 743 | else 744 | col = color; 745 | } else { 746 | if (plane0_r & 0x80000000) 747 | col = mm0_color; 748 | else 749 | continue; 750 | } 751 | if (q[i]) 752 | spr_coll |= q[i] | sbit; 753 | else { 754 | p[i] = col; 755 | q[i] = sbit; 756 | } 757 | } 758 | 759 | } else { // Standard mode 760 | 761 | // Expand sprite data 762 | sdata_l = ExpTable[sdata >> 24 & 0xff] << 16 | ExpTable[sdata >> 16 & 0xff]; 763 | sdata_r = ExpTable[sdata >> 8 & 0xff] << 16; 764 | 765 | // Collision with graphics? 766 | if ((fore_mask & sdata_l) || (fore_mask_r & sdata_r)) { 767 | gfx_coll |= sbit; 768 | if (mdp & sbit) { 769 | sdata_l &= ~fore_mask; // Mask sprite if in background 770 | sdata_r &= ~fore_mask_r; 771 | } 772 | } 773 | 774 | // Paint sprite 775 | for (i=0; i<32; i++, sdata_l<<=1) 776 | if (sdata_l & 0x80000000) { 777 | if (q[i]) // Collision with sprite? 778 | spr_coll |= q[i] | sbit; 779 | else { // Draw pixel if no collision 780 | p[i] = color; 781 | q[i] = sbit; 782 | } 783 | } 784 | for (; i<48; i++, sdata_r<<=1) 785 | if (sdata_r & 0x80000000) { 786 | if (q[i]) // Collision with sprite? 787 | spr_coll |= q[i] | sbit; 788 | else { // Draw pixel if no collision 789 | p[i] = color; 790 | q[i] = sbit; 791 | } 792 | } 793 | } 794 | 795 | } else { // Unexpanded 796 | 797 | if (mmc & sbit) { // Multicolor mode 798 | uint32_t plane0, plane1; 799 | 800 | // Convert sprite chunky pixels to bitplanes 801 | plane0 = (sdata & 0x55555555) | (sdata & 0x55555555) << 1; 802 | plane1 = (sdata & 0xaaaaaaaa) | (sdata & 0xaaaaaaaa) >> 1; 803 | 804 | // Collision with graphics? 805 | if (fore_mask & (plane0 | plane1)) { 806 | gfx_coll |= sbit; 807 | if (mdp & sbit) { 808 | plane0 &= ~fore_mask; // Mask sprite if in background 809 | plane1 &= ~fore_mask; 810 | } 811 | } 812 | 813 | // Paint sprite 814 | for (i=0; i<24; i++, plane0<<=1, plane1<<=1) { 815 | uint32_t col; 816 | if (plane1 & 0x80000000) { 817 | if (plane0 & 0x80000000) 818 | col = mm1_color; 819 | else 820 | col = color; 821 | } else { 822 | if (plane0 & 0x80000000) 823 | col = mm0_color; 824 | else 825 | continue; 826 | } 827 | if (q[i]) 828 | spr_coll |= q[i] | sbit; 829 | else { 830 | p[i] = col; 831 | q[i] = sbit; 832 | } 833 | } 834 | 835 | } else { // Standard mode 836 | 837 | // Collision with graphics? 838 | if (fore_mask & sdata) { 839 | gfx_coll |= sbit; 840 | if (mdp & sbit) 841 | sdata &= ~fore_mask; // Mask sprite if in background 842 | } 843 | 844 | // Paint sprite 845 | for (i=0; i<24; i++, sdata<<=1) 846 | if (sdata & 0x80000000) { 847 | if (q[i]) { // Collision with sprite? 848 | spr_coll |= q[i] | sbit; 849 | } else { // Draw pixel if no collision 850 | p[i] = color; 851 | q[i] = sbit; 852 | } 853 | } 854 | } 855 | } 856 | } 857 | } 858 | 859 | if (SpriteCollisions) { 860 | 861 | // Check sprite-sprite collisions 862 | if (clx_spr) 863 | clx_spr |= spr_coll; 864 | else { 865 | clx_spr |= spr_coll; 866 | irq_flag |= 0x04; 867 | if (irq_mask & 0x04) { 868 | irq_flag |= 0x80; 869 | vic_trigger_irq(); 870 | } 871 | } 872 | 873 | // Check sprite-background collisions 874 | if (clx_bgr) 875 | clx_bgr |= gfx_coll; 876 | else { 877 | clx_bgr |= gfx_coll; 878 | irq_flag |= 0x02; 879 | if (irq_mask & 0x02) { 880 | irq_flag |= 0x80; 881 | vic_trigger_irq(); 882 | } 883 | } 884 | } 885 | } 886 | 887 | 888 | /* 889 | * Emulate one clock cycle, returns true if new raster line has started 890 | */ 891 | 892 | // Set BA low 893 | #define SetBALow \ 894 | if (!vic_ba_Low) { \ 895 | first_ba_cycle = cycle_counter; \ 896 | vic_ba_Low = true; \ 897 | } 898 | 899 | // Turn on display if Bad Line 900 | #define DisplayIfBadLine \ 901 | if (is_bad_line) \ 902 | display_state = true; 903 | 904 | // Turn on display and matrix access if Bad Line 905 | #define FetchIfBadLine \ 906 | if (is_bad_line) { \ 907 | display_state = true; \ 908 | SetBALow; \ 909 | } 910 | 911 | // Turn on display and matrix access and reset RC if Bad Line 912 | #define RCIfBadLine \ 913 | if (is_bad_line) { \ 914 | display_state = true; \ 915 | rc = 0; \ 916 | SetBALow; \ 917 | } 918 | 919 | // Idle access 920 | #define IdleAccess \ 921 | read_byte(0x3fff) 922 | 923 | // Refresh access 924 | #define RefreshAccess \ 925 | read_byte(0x3f00 | ref_cnt--) 926 | 927 | // Turn on sprite DMA if necessary 928 | #define CheckSpriteDMA \ 929 | mask = 1; \ 930 | for (i=0; i<8; i++, mask<<=1) \ 931 | if ((me & mask) && (raster_y & 0xff) == my[i]) { \ 932 | spr_dma_on |= mask; \ 933 | mc_base[i] = 0; \ 934 | if (mye & mask) \ 935 | spr_exp_y &= ~mask; \ 936 | } 937 | 938 | // Fetch sprite data pointer 939 | #define SprPtrAccess(num) \ 940 | spr_ptr[num] = read_byte(matrix_base | 0x03f8 | num) << 6; 941 | 942 | // Fetch sprite data, increment data counter 943 | #define SprDataAccess(num, bytenum) \ 944 | if (spr_dma_on & (1 << num)) { \ 945 | spr_data[num][bytenum] = read_byte((mc[num] & 0x3f) | spr_ptr[num]); \ 946 | mc[num]++; \ 947 | } else if (bytenum == 1) \ 948 | IdleAccess; 949 | 950 | // Sample border color and increment chunky_ptr and fore_mask_ptr 951 | #define SampleBorder \ 952 | if (draw_this_line) { \ 953 | if (border_on) \ 954 | border_color_sample[cycle-13] = ec_color; \ 955 | chunky_ptr += 8; \ 956 | fore_mask_ptr++; \ 957 | } 958 | 959 | 960 | static bool emulate_cycle_6569(void) 961 | { 962 | uint8_t mask; 963 | int i; 964 | 965 | switch (cycle) { 966 | 967 | // Fetch sprite pointer 3, increment raster counter, trigger raster IRQ, 968 | // test for Bad Line, reset BA if sprites 3 and 4 off, read data of sprite 3 969 | case 1: 970 | if (raster_y == TOTAL_RASTERS-1) 971 | 972 | // Trigger VBlank in cycle 2 973 | vblanking = true; 974 | 975 | else { 976 | 977 | // Increment raster counter 978 | raster_y++; 979 | 980 | // Trigger raster IRQ if IRQ line reached 981 | if (raster_y == irq_raster) 982 | raster_irq(); 983 | 984 | // In line $30, the DEN bit controls if Bad Lines can occur 985 | if (raster_y == 0x30) 986 | bad_lines_enabled = ctrl1 & 0x10; 987 | 988 | // Bad Line condition? 989 | is_bad_line = (raster_y >= FIRST_DMA_LINE && raster_y <= LAST_DMA_LINE && ((raster_y & 7) == y_scroll) && bad_lines_enabled); 990 | 991 | // Don't draw all lines, hide some at the top and bottom 992 | draw_this_line = (raster_y >= FIRST_DISP_LINE && raster_y <= LAST_DISP_LINE); 993 | } 994 | 995 | // First sample of border state 996 | border_on_sample[0] = border_on; 997 | 998 | SprPtrAccess(3); 999 | SprDataAccess(3, 0); 1000 | DisplayIfBadLine; 1001 | if (!(spr_dma_on & 0x18)) 1002 | vic_ba_Low = false; 1003 | break; 1004 | 1005 | // Set BA for sprite 5, read data of sprite 3 1006 | case 2: 1007 | if (vblanking) { 1008 | 1009 | // Vertical blank, reset counters 1010 | raster_y = vc_base = 0; 1011 | ref_cnt = 0xff; 1012 | lp_triggered = vblanking = false; 1013 | 1014 | vic_vblank(); 1015 | 1016 | // Get bitmap pointer for next frame. This must be done 1017 | // after calling the vblank because the preferences 1018 | // and screen configuration may have been changed there 1019 | chunky_line_start = display_bitmap_base; 1020 | xmod = display_bitmap_xmod; 1021 | 1022 | // Trigger raster IRQ if IRQ in line 0 1023 | if (irq_raster == 0) 1024 | raster_irq(); 1025 | 1026 | } 1027 | 1028 | // Our output goes here 1029 | chunky_ptr = chunky_line_start; 1030 | 1031 | // Clear foreground mask 1032 | memset(fore_mask_buf, 0, DISPLAY_X/8); 1033 | fore_mask_ptr = fore_mask_buf; 1034 | 1035 | SprDataAccess(3,1); 1036 | SprDataAccess(3,2); 1037 | DisplayIfBadLine; 1038 | if (spr_dma_on & 0x20) 1039 | SetBALow; 1040 | break; 1041 | 1042 | // Fetch sprite pointer 4, reset BA is sprite 4 and 5 off 1043 | case 3: 1044 | SprPtrAccess(4); 1045 | SprDataAccess(4, 0); 1046 | DisplayIfBadLine; 1047 | if (!(spr_dma_on & 0x30)) 1048 | vic_ba_Low = false; 1049 | break; 1050 | 1051 | // Set BA for sprite 6, read data of sprite 4 1052 | case 4: 1053 | SprDataAccess(4, 1); 1054 | SprDataAccess(4, 2); 1055 | DisplayIfBadLine; 1056 | if (spr_dma_on & 0x40) 1057 | SetBALow; 1058 | break; 1059 | 1060 | // Fetch sprite pointer 5, reset BA if sprite 5 and 6 off 1061 | case 5: 1062 | SprPtrAccess(5); 1063 | SprDataAccess(5, 0); 1064 | DisplayIfBadLine; 1065 | if (!(spr_dma_on & 0x60)) 1066 | vic_ba_Low = false; 1067 | break; 1068 | 1069 | // Set BA for sprite 7, read data of sprite 5 1070 | case 6: 1071 | SprDataAccess(5, 1); 1072 | SprDataAccess(5, 2); 1073 | DisplayIfBadLine; 1074 | if (spr_dma_on & 0x80) 1075 | SetBALow; 1076 | break; 1077 | 1078 | // Fetch sprite pointer 6, reset BA if sprite 6 and 7 off 1079 | case 7: 1080 | SprPtrAccess(6); 1081 | SprDataAccess(6, 0); 1082 | DisplayIfBadLine; 1083 | if (!(spr_dma_on & 0xc0)) 1084 | vic_ba_Low = false; 1085 | break; 1086 | 1087 | // Read data of sprite 6 1088 | case 8: 1089 | SprDataAccess(6, 1); 1090 | SprDataAccess(6, 2); 1091 | DisplayIfBadLine; 1092 | break; 1093 | 1094 | // Fetch sprite pointer 7, reset BA if sprite 7 off 1095 | case 9: 1096 | SprPtrAccess(7); 1097 | SprDataAccess(7, 0); 1098 | DisplayIfBadLine; 1099 | if (!(spr_dma_on & 0x80)) 1100 | vic_ba_Low = false; 1101 | break; 1102 | 1103 | // Read data of sprite 7 1104 | case 10: 1105 | SprDataAccess(7, 1); 1106 | SprDataAccess(7, 2); 1107 | DisplayIfBadLine; 1108 | break; 1109 | 1110 | // Refresh, reset BA 1111 | case 11: 1112 | RefreshAccess; 1113 | DisplayIfBadLine; 1114 | vic_ba_Low = false; 1115 | break; 1116 | 1117 | // Refresh, turn on matrix access if Bad Line 1118 | case 12: 1119 | RefreshAccess; 1120 | FetchIfBadLine; 1121 | break; 1122 | 1123 | // Refresh, turn on matrix access if Bad Line, reset raster_x, graphics display starts here 1124 | case 13: 1125 | draw_background(); 1126 | SampleBorder; 1127 | RefreshAccess; 1128 | FetchIfBadLine; 1129 | raster_x = 0xfffc; 1130 | break; 1131 | 1132 | // Refresh, VCBASE->VCCOUNT, turn on matrix access and reset RC if Bad Line 1133 | case 14: 1134 | draw_background(); 1135 | SampleBorder; 1136 | RefreshAccess; 1137 | RCIfBadLine; 1138 | vc = vc_base; 1139 | break; 1140 | 1141 | // Refresh and matrix access, increment mc_base by 2 if y expansion flipflop is set 1142 | case 15: 1143 | draw_background(); 1144 | SampleBorder; 1145 | RefreshAccess; 1146 | FetchIfBadLine; 1147 | 1148 | for (i=0; i<8; i++) 1149 | if (spr_exp_y & (1 << i)) 1150 | mc_base[i] += 2; 1151 | 1152 | ml_index = 0; 1153 | matrix_access(); 1154 | break; 1155 | 1156 | // Graphics and matrix access, increment mc_base by 1 if y expansion flipflop is set 1157 | // and check if sprite DMA can be turned off 1158 | case 16: 1159 | draw_background(); 1160 | SampleBorder; 1161 | graphics_access(); 1162 | FetchIfBadLine; 1163 | 1164 | mask = 1; 1165 | for (i=0; i<8; i++, mask<<=1) { 1166 | if (spr_exp_y & mask) 1167 | mc_base[i]++; 1168 | if ((mc_base[i] & 0x3f) == 0x3f) 1169 | spr_dma_on &= ~mask; 1170 | } 1171 | 1172 | matrix_access(); 1173 | break; 1174 | 1175 | // Graphics and matrix access, turn off border in 40 column mode, display window starts here 1176 | case 17: 1177 | if (ctrl2 & 8) { 1178 | if (raster_y == dy_stop) 1179 | ud_border_on = true; 1180 | else { 1181 | if (ctrl1 & 0x10) { 1182 | if (raster_y == dy_start) 1183 | border_on = ud_border_on = false; 1184 | else 1185 | if (!ud_border_on) 1186 | border_on = false; 1187 | } else 1188 | if (!ud_border_on) 1189 | border_on = false; 1190 | } 1191 | } 1192 | 1193 | // Second sample of border state 1194 | border_on_sample[1] = border_on; 1195 | 1196 | draw_background(); 1197 | draw_graphics(); 1198 | SampleBorder; 1199 | graphics_access(); 1200 | FetchIfBadLine; 1201 | matrix_access(); 1202 | break; 1203 | 1204 | // Turn off border in 38 column mode 1205 | case 18: 1206 | if (!(ctrl2 & 8)) { 1207 | if (raster_y == dy_stop) 1208 | ud_border_on = true; 1209 | else { 1210 | if (ctrl1 & 0x10) { 1211 | if (raster_y == dy_start) 1212 | border_on = ud_border_on = false; 1213 | else 1214 | if (!ud_border_on) 1215 | border_on = false; 1216 | } else 1217 | if (!ud_border_on) 1218 | border_on = false; 1219 | } 1220 | } 1221 | 1222 | // Third sample of border state 1223 | border_on_sample[2] = border_on; 1224 | 1225 | // Falls through 1226 | 1227 | // Graphics and matrix access 1228 | case 19: case 20: case 21: case 22: case 23: case 24: 1229 | case 25: case 26: case 27: case 28: case 29: case 30: 1230 | case 31: case 32: case 33: case 34: case 35: case 36: 1231 | case 37: case 38: case 39: case 40: case 41: case 42: 1232 | case 43: case 44: case 45: case 46: case 47: case 48: 1233 | case 49: case 50: case 51: case 52: case 53: case 54: // Gnagna... 1234 | draw_graphics(); 1235 | SampleBorder; 1236 | graphics_access(); 1237 | FetchIfBadLine; 1238 | matrix_access(); 1239 | last_char_data = char_data; 1240 | break; 1241 | 1242 | // Last graphics access, turn off matrix access, turn on sprite DMA if Y coordinate is 1243 | // right and sprite is enabled, handle sprite y expansion, set BA for sprite 0 1244 | case 55: 1245 | draw_graphics(); 1246 | SampleBorder; 1247 | graphics_access(); 1248 | DisplayIfBadLine; 1249 | 1250 | // Invert y expansion flipflop if bit in MYE is set 1251 | mask = 1; 1252 | for (i=0; i<8; i++, mask<<=1) 1253 | if (mye & mask) 1254 | spr_exp_y ^= mask; 1255 | CheckSpriteDMA; 1256 | 1257 | if (spr_dma_on & 0x01) { // Don't remove these braces! 1258 | SetBALow; 1259 | } else 1260 | vic_ba_Low = false; 1261 | break; 1262 | 1263 | // Turn on border in 38 column mode, turn on sprite DMA if Y coordinate is right and 1264 | // sprite is enabled, set BA for sprite 0, display window ends here 1265 | case 56: 1266 | if (!(ctrl2 & 8)) 1267 | border_on = true; 1268 | 1269 | // Fourth sample of border state 1270 | border_on_sample[3] = border_on; 1271 | 1272 | draw_graphics(); 1273 | SampleBorder; 1274 | IdleAccess; 1275 | DisplayIfBadLine; 1276 | CheckSpriteDMA; 1277 | 1278 | if (spr_dma_on & 0x01) 1279 | SetBALow; 1280 | break; 1281 | 1282 | // Turn on border in 40 column mode, set BA for sprite 1, paint sprites 1283 | case 57: 1284 | if (ctrl2 & 8) 1285 | border_on = true; 1286 | 1287 | // Fifth sample of border state 1288 | border_on_sample[4] = border_on; 1289 | 1290 | // Sample spr_disp_on and spr_data for sprite drawing 1291 | if ((spr_draw = spr_disp_on)) 1292 | memcpy(spr_draw_data, spr_data, 8*4); 1293 | 1294 | // Turn off sprite display if DMA is off 1295 | mask = 1; 1296 | for (i=0; i<8; i++, mask<<=1) 1297 | if ((spr_disp_on & mask) && !(spr_dma_on & mask)) 1298 | spr_disp_on &= ~mask; 1299 | 1300 | draw_background(); 1301 | SampleBorder; 1302 | IdleAccess; 1303 | DisplayIfBadLine; 1304 | if (spr_dma_on & 0x02) 1305 | SetBALow; 1306 | break; 1307 | 1308 | // Fetch sprite pointer 0, mc_base->mc, turn on sprite display if necessary, 1309 | // turn off display if RC=7, read data of sprite 0 1310 | case 58: 1311 | draw_background(); 1312 | SampleBorder; 1313 | 1314 | mask = 1; 1315 | for (i=0; i<8; i++, mask<<=1) { 1316 | mc[i] = mc_base[i]; 1317 | if ((spr_dma_on & mask) && (raster_y & 0xff) == my[i]) 1318 | spr_disp_on |= mask; 1319 | } 1320 | SprPtrAccess(0); 1321 | SprDataAccess(0, 0); 1322 | 1323 | if (rc == 7) { 1324 | vc_base = vc; 1325 | display_state = false; 1326 | } 1327 | if (is_bad_line || display_state) { 1328 | display_state = true; 1329 | rc = (rc + 1) & 7; 1330 | } 1331 | break; 1332 | 1333 | // Set BA for sprite 2, read data of sprite 0 1334 | case 59: 1335 | draw_background(); 1336 | SampleBorder; 1337 | SprDataAccess(0, 1); 1338 | SprDataAccess(0, 2); 1339 | DisplayIfBadLine; 1340 | if (spr_dma_on & 0x04) 1341 | SetBALow; 1342 | break; 1343 | 1344 | // Fetch sprite pointer 1, reset BA if sprite 1 and 2 off, graphics display ends here 1345 | case 60: 1346 | draw_background(); 1347 | SampleBorder; 1348 | 1349 | if (draw_this_line) { 1350 | 1351 | // Draw sprites 1352 | if (spr_draw && SpritesOn) 1353 | draw_sprites(); 1354 | 1355 | // Draw border 1356 | if (border_on_sample[0]) 1357 | for (i=0; i<4; i++) 1358 | memset8(chunky_line_start+i*8, border_color_sample[i]); 1359 | if (border_on_sample[1]) 1360 | memset8(chunky_line_start+4*8, border_color_sample[4]); 1361 | if (border_on_sample[2]) 1362 | for (i=5; i<43; i++) 1363 | memset8(chunky_line_start+i*8, border_color_sample[i]); 1364 | if (border_on_sample[3]) 1365 | memset8(chunky_line_start+43*8, border_color_sample[43]); 1366 | if (border_on_sample[4]) 1367 | for (i=44; i 13 | 14 | 15 | /* 16 | * Resonance frequency polynomials 17 | */ 18 | 19 | #define CALC_RESONANCE_LP(f) (227.755\ 20 | - 1.7635 * f\ 21 | - 0.0176385 * f * f\ 22 | + 0.00333484 * f * f * f\ 23 | - 9.05683E-6 * f * f * f * f) 24 | 25 | #define CALC_RESONANCE_HP(f) (366.374\ 26 | - 14.0052 * f\ 27 | + 0.603212 * f * f\ 28 | - 0.000880196 * f * f * f) 29 | 30 | 31 | /* 32 | * Random number generator for noise waveform 33 | */ 34 | 35 | static uint8_t sid_random(void); 36 | static uint8_t sid_random(void) 37 | { 38 | static uint32_t seed = 1; 39 | seed = seed * 1103515245 + 12345; 40 | return seed >> 16; 41 | } 42 | 43 | 44 | /* 45 | * Constructor 46 | */ 47 | 48 | static void init6581() 49 | { 50 | // Link voices together 51 | voice[0].mod_by = &voice[2]; 52 | voice[1].mod_by = &voice[0]; 53 | voice[2].mod_by = &voice[1]; 54 | voice[0].mod_to = &voice[1]; 55 | voice[1].mod_to = &voice[2]; 56 | voice[2].mod_to = &voice[0]; 57 | 58 | // Calculate triangle table 59 | for (int i=0; i<0x1000; i++) { 60 | TriTable[i] = (i << 4) | (i >> 8); 61 | TriTable[0x1fff-i] = (i << 4) | (i >> 8); 62 | } 63 | 64 | reset6581(); 65 | 66 | // Stuff the current register values into the renderer 67 | for (int i=0; i<25; i++) 68 | write_register_6581(i, regs[i]); 69 | } 70 | 71 | 72 | /* 73 | * Reset the SID 74 | */ 75 | 76 | static void reset6581(void) 77 | { 78 | for (int i=0; i<32; i++) 79 | regs[i] = 0; 80 | last_sid_byte = 0; 81 | 82 | // Reset the renderer 83 | volume = 0; 84 | v3_mute = false; 85 | 86 | for (int v=0; v<3; v++) { 87 | voice[v].wave = WAVE_NONE; 88 | voice[v].eg_state = EG_IDLE; 89 | voice[v].count = voice[v].add = 0; 90 | voice[v].freq = voice[v].pw = 0; 91 | voice[v].eg_level = voice[v].s_level = 0; 92 | voice[v].a_add = voice[v].d_sub = voice[v].r_sub = EGTable[0]; 93 | voice[v].gate = voice[v].ring = voice[v].test = false; 94 | voice[v].filter = voice[v].sync = false; 95 | } 96 | 97 | f_type = FILT_NONE; 98 | f_freq = f_res = 0; 99 | f_ampl = 1.0; 100 | d1 = d2 = g1 = g2 = 0.0; 101 | xn1 = xn2 = yn1 = yn2 = 0.0; 102 | 103 | sample_in_ptr = 0; 104 | memset(sample_buf, 0, SAMPLE_BUF_SIZE); 105 | } 106 | 107 | #if 0 108 | /* 109 | * Get SID state 110 | */ 111 | 112 | static void get_state_6581(MOS6581State *ss) 113 | { 114 | ss->freq_lo_1 = regs[0]; 115 | ss->freq_hi_1 = regs[1]; 116 | ss->pw_lo_1 = regs[2]; 117 | ss->pw_hi_1 = regs[3]; 118 | ss->ctrl_1 = regs[4]; 119 | ss->AD_1 = regs[5]; 120 | ss->SR_1 = regs[6]; 121 | 122 | ss->freq_lo_2 = regs[7]; 123 | ss->freq_hi_2 = regs[8]; 124 | ss->pw_lo_2 = regs[9]; 125 | ss->pw_hi_2 = regs[10]; 126 | ss->ctrl_2 = regs[11]; 127 | ss->AD_2 = regs[12]; 128 | ss->SR_2 = regs[13]; 129 | 130 | ss->freq_lo_3 = regs[14]; 131 | ss->freq_hi_3 = regs[15]; 132 | ss->pw_lo_3 = regs[16]; 133 | ss->pw_hi_3 = regs[17]; 134 | ss->ctrl_3 = regs[18]; 135 | ss->AD_3 = regs[19]; 136 | ss->SR_3 = regs[20]; 137 | 138 | ss->fc_lo = regs[21]; 139 | ss->fc_hi = regs[22]; 140 | ss->res_filt = regs[23]; 141 | ss->mode_vol = regs[24]; 142 | 143 | ss->pot_x = 0xff; 144 | ss->pot_y = 0xff; 145 | ss->osc_3 = 0; 146 | ss->env_3 = 0; 147 | } 148 | #endif 149 | 150 | /* 151 | * Fill buffer (for Unix sound routines), sample volume (for sampled voice) 152 | */ 153 | 154 | inline static void emulate_line_6581(void) 155 | { 156 | emulate_line_sound(); 157 | } 158 | 159 | 160 | /* 161 | * Read from register 162 | */ 163 | 164 | inline static uint8_t read_register_6581(uint16_t adr) 165 | { 166 | // A/D converters 167 | if (adr == 0x19 || adr == 0x1a) { 168 | last_sid_byte = 0; 169 | return 0xff; 170 | } 171 | 172 | // Voice 3 oscillator/EG readout 173 | if (adr == 0x1b || adr == 0x1c) { 174 | last_sid_byte = 0; 175 | return rand(); 176 | } 177 | 178 | // Write-only register: Return last value written to SID 179 | return last_sid_byte; 180 | } 181 | 182 | 183 | /* 184 | * Write to register 185 | */ 186 | 187 | inline static void write_register_6581(uint16_t adr, uint8_t byte) 188 | { 189 | // Keep a local copy of the register values 190 | last_sid_byte = regs[adr] = byte; 191 | 192 | if (!ready) 193 | return; 194 | 195 | int v = adr/7; // Voice number 196 | 197 | switch (adr) { 198 | case 0: 199 | case 7: 200 | case 14: 201 | voice[v].freq = (voice[v].freq & 0xff00) | byte; 202 | voice[v].add = (uint32_t)((float)voice[v].freq * SID_FREQ / SAMPLE_FREQ); 203 | break; 204 | 205 | case 1: 206 | case 8: 207 | case 15: 208 | voice[v].freq = (voice[v].freq & 0xff) | (byte << 8); 209 | voice[v].add = (uint32_t)((float)voice[v].freq * SID_FREQ / SAMPLE_FREQ); 210 | break; 211 | 212 | case 2: 213 | case 9: 214 | case 16: 215 | voice[v].pw = (voice[v].pw & 0x0f00) | byte; 216 | break; 217 | 218 | case 3: 219 | case 10: 220 | case 17: 221 | voice[v].pw = (voice[v].pw & 0xff) | ((byte & 0xf) << 8); 222 | break; 223 | 224 | case 4: 225 | case 11: 226 | case 18: 227 | voice[v].wave = (byte >> 4) & 0xf; 228 | if ((byte & 1) != voice[v].gate) 229 | { 230 | if (byte & 1) // Gate turned on 231 | voice[v].eg_state = EG_ATTACK; 232 | else // Gate turned off 233 | if (voice[v].eg_state != EG_IDLE) 234 | voice[v].eg_state = EG_RELEASE; 235 | } 236 | voice[v].gate = byte & 1; 237 | voice[v].mod_by->sync = byte & 2; 238 | voice[v].ring = byte & 4; 239 | if ((voice[v].test = byte & 8)) 240 | voice[v].count = 0; 241 | break; 242 | 243 | case 5: 244 | case 12: 245 | case 19: 246 | voice[v].a_add = EGTable[byte >> 4]; 247 | voice[v].d_sub = EGTable[byte & 0xf]; 248 | break; 249 | 250 | case 6: 251 | case 13: 252 | case 20: 253 | voice[v].s_level = (byte >> 4) * 0x111111; 254 | voice[v].r_sub = EGTable[byte & 0xf]; 255 | break; 256 | 257 | case 22: 258 | if (byte != f_freq) { 259 | f_freq = byte; 260 | if (SIDFilters) 261 | calc_filter(); 262 | } 263 | break; 264 | 265 | case 23: 266 | voice[0].filter = byte & 1; 267 | voice[1].filter = byte & 2; 268 | voice[2].filter = byte & 4; 269 | if ((byte >> 4) != f_res) { 270 | f_res = byte >> 4; 271 | if (SIDFilters) 272 | calc_filter(); 273 | } 274 | break; 275 | 276 | case 24: 277 | volume = byte & 0xf; 278 | v3_mute = byte & 0x80; 279 | if (((byte >> 4) & 7) != f_type) { 280 | f_type = (byte >> 4) & 7; 281 | xn1 = xn2 = yn1 = yn2 = 0.0; 282 | if (SIDFilters) 283 | calc_filter(); 284 | } 285 | break; 286 | } 287 | } 288 | 289 | 290 | /* 291 | * Calculate IIR filter coefficients 292 | */ 293 | 294 | static void calc_filter(void) 295 | { 296 | float fr, arg; 297 | 298 | // Check for some trivial cases 299 | if (f_type == FILT_ALL) { 300 | d1 = 0.0; d2 = 0.0; 301 | g1 = 0.0; g2 = 0.0; 302 | f_ampl = 1.0; 303 | return; 304 | } else if (f_type == FILT_NONE) { 305 | d1 = 0.0; d2 = 0.0; 306 | g1 = 0.0; g2 = 0.0; 307 | f_ampl = 0.0; 308 | return; 309 | } 310 | 311 | // Calculate resonance frequency 312 | if (f_type == FILT_LP || f_type == FILT_LPBP) 313 | fr = CALC_RESONANCE_LP(f_freq); 314 | else 315 | fr = CALC_RESONANCE_HP(f_freq); 316 | 317 | // Limit to <1/2 sample frequency, avoid div by 0 in case FILT_BP below 318 | arg = fr / (float)(SAMPLE_FREQ >> 1); 319 | if (arg > 0.99) 320 | arg = 0.99; 321 | if (arg < 0.01) 322 | arg = 0.01; 323 | 324 | // Calculate poles (resonance frequency and resonance) 325 | g2 = 0.55 + 1.2 * arg * arg - 1.2 * arg + (float)f_res * 0.0133333333; 326 | g1 = -2.0 * sqrt(g2) * cos(M_PI * arg); 327 | 328 | // Increase resonance if LP/HP combined with BP 329 | if (f_type == FILT_LPBP || f_type == FILT_HPBP) 330 | g2 += 0.1; 331 | 332 | // Stabilize filter 333 | if (fabs(g1) >= g2 + 1.0) 334 | { 335 | if (g1 > 0.0) 336 | g1 = g2 + 0.99; 337 | else 338 | g1 = -(g2 + 0.99); 339 | } 340 | 341 | // Calculate roots (filter characteristic) and input attenuation 342 | switch (f_type) { 343 | 344 | case FILT_LPBP: 345 | case FILT_LP: 346 | d1 = 2.0; d2 = 1.0; 347 | f_ampl = 0.25 * (1.0 + g1 + g2); 348 | break; 349 | 350 | case FILT_HPBP: 351 | case FILT_HP: 352 | d1 = -2.0; d2 = 1.0; 353 | f_ampl = 0.25 * (1.0 - g1 + g2); 354 | break; 355 | 356 | case FILT_BP: 357 | d1 = 0.0; d2 = -1.0; 358 | f_ampl = 0.25 * (1.0 + g1 + g2) * (1 + cos(M_PI * arg)) / sin(M_PI * arg); 359 | break; 360 | 361 | case FILT_NOTCH: 362 | d1 = -2.0 * cos(M_PI * arg); d2 = 1.0; 363 | f_ampl = 0.25 * (1.0 + g1 + g2) * (1 + cos(M_PI * arg)) / (sin(M_PI * arg)); 364 | break; 365 | 366 | default: 367 | break; 368 | } 369 | } 370 | 371 | 372 | /* 373 | * Fill one audio buffer with calculated SID sound 374 | */ 375 | 376 | static void calc_buffer(int16_t *buf, long count) 377 | { 378 | // Get filter coefficients, so the emulator won't change 379 | // them in the middle of our calculations 380 | float cf_ampl = f_ampl; 381 | float cd1 = d1, cd2 = d2, cg1 = g1, cg2 = g2; 382 | 383 | // Index in sample_buf for reading, 16.16 fixed 384 | uint32_t sample_count = (sample_in_ptr + SAMPLE_BUF_SIZE/2) << 16; 385 | 386 | count >>= 1; // 16 bit mono output, count is in bytes 387 | 388 | while (count--) { 389 | int32_t sum_output; 390 | int32_t sum_output_filter = 0; 391 | 392 | // Get current master volume from sample buffer, 393 | // calculate sampled voice 394 | uint8_t master_volume = sample_buf[(sample_count >> 16) % SAMPLE_BUF_SIZE]; 395 | sample_count += ((0x138 * 50) << 16) / SAMPLE_FREQ; 396 | sum_output = SampleTab[master_volume] << 8; 397 | 398 | // Loop for all three voices 399 | for (int j=0; j<3; j++) { 400 | DRVoice *v = &voice[j]; 401 | 402 | // Envelope generators 403 | uint16_t envelope; 404 | 405 | switch (v->eg_state) { 406 | case EG_ATTACK: 407 | v->eg_level += v->a_add; 408 | if (v->eg_level > 0xffffff) { 409 | v->eg_level = 0xffffff; 410 | v->eg_state = EG_DECAY; 411 | } 412 | break; 413 | case EG_DECAY: 414 | if (v->eg_level <= v->s_level || v->eg_level > 0xffffff) 415 | v->eg_level = v->s_level; 416 | else { 417 | v->eg_level -= v->d_sub >> EGDRShift[v->eg_level >> 16]; 418 | if (v->eg_level <= v->s_level || v->eg_level > 0xffffff) 419 | v->eg_level = v->s_level; 420 | } 421 | break; 422 | case EG_RELEASE: 423 | v->eg_level -= v->r_sub >> EGDRShift[v->eg_level >> 16]; 424 | if (v->eg_level > 0xffffff) { 425 | v->eg_level = 0; 426 | v->eg_state = EG_IDLE; 427 | } 428 | break; 429 | case EG_IDLE: 430 | v->eg_level = 0; 431 | break; 432 | } 433 | envelope = (v->eg_level * master_volume) >> 20; 434 | 435 | // Waveform generator 436 | uint16_t output; 437 | 438 | if (!v->test) 439 | v->count += v->add; 440 | 441 | if (v->sync && (v->count > 0x1000000)) 442 | v->mod_to->count = 0; 443 | 444 | v->count &= 0xffffff; 445 | 446 | switch (v->wave) { 447 | case WAVE_TRI: 448 | if (v->ring) 449 | output = TriTable[(v->count ^ (v->mod_by->count & 0x800000)) >> 11]; 450 | else 451 | output = TriTable[v->count >> 11]; 452 | break; 453 | case WAVE_SAW: 454 | output = v->count >> 8; 455 | break; 456 | case WAVE_RECT: 457 | if (v->count > (uint32_t)(v->pw << 12)) 458 | output = 0xffff; 459 | else 460 | output = 0; 461 | break; 462 | case WAVE_TRISAW: 463 | output = TriSawTable[v->count >> 16]; 464 | break; 465 | case WAVE_TRIRECT: 466 | if (v->count > (uint32_t)(v->pw << 12)) 467 | output = TriRectTable[v->count >> 16]; 468 | else 469 | output = 0; 470 | break; 471 | case WAVE_SAWRECT: 472 | if (v->count > (uint32_t)(v->pw << 12)) 473 | output = SawRectTable[v->count >> 16]; 474 | else 475 | output = 0; 476 | break; 477 | case WAVE_TRISAWRECT: 478 | if (v->count > (uint32_t)(v->pw << 12)) 479 | output = TriSawRectTable[v->count >> 16]; 480 | else 481 | output = 0; 482 | break; 483 | case WAVE_NOISE: 484 | if (v->count > 0x100000) { 485 | output = v->noise = sid_random() << 8; 486 | v->count &= 0xfffff; 487 | } else 488 | output = v->noise; 489 | break; 490 | default: 491 | output = 0x8000; 492 | break; 493 | } 494 | if (v->filter) 495 | sum_output_filter += (int16_t)(output ^ 0x8000) * envelope; 496 | else 497 | sum_output += (int16_t)(output ^ 0x8000) * envelope; 498 | } 499 | 500 | // Filter 501 | if (SIDFilters) { 502 | float xn = (float)sum_output_filter * cf_ampl; 503 | float yn = xn + cd1 * xn1 + cd2 * xn2 - cg1 * yn1 - cg2 * yn2; 504 | yn2 = yn1; yn1 = yn; xn2 = xn1; xn1 = xn; 505 | sum_output_filter = (int32_t)yn; 506 | } 507 | 508 | // Write to buffer 509 | *buf++ = (sum_output + sum_output_filter) >> 10; 510 | } 511 | } 512 | 513 | -------------------------------------------------------------------------------- /pi/6581.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Based on SID.h - 6581 emulation 3 | * 4 | * Frodo (C) 1994-1997,2002 Christian Bauer 5 | */ 6 | 7 | // Define this if you want an emulation of an 8580 8 | // (affects combined waveforms) 9 | #undef EMUL_MOS8580 10 | 11 | static const uint32_t SAMPLE_FREQ = 44100; // Sample output frequency in Hz 12 | static const uint32_t SID_FREQ = 985248; // SID frequency in Hz 13 | static const uint32_t CALC_FREQ = 50; // Frequency at which calc_buffer is called in Hz (should be 50Hz) 14 | static const uint32_t SID_CYCLES = SID_FREQ/SAMPLE_FREQ; // # of SID clocks per sample frame 15 | static const int SAMPLE_BUF_SIZE = 0x138*2;// Size of buffer for sampled voice (double buffered) 16 | 17 | static const bool SIDFilters = true; // Emulate SID filters 18 | 19 | // SID state 20 | struct MOS6581State { 21 | uint8_t freq_lo_1; 22 | uint8_t freq_hi_1; 23 | uint8_t pw_lo_1; 24 | uint8_t pw_hi_1; 25 | uint8_t ctrl_1; 26 | uint8_t AD_1; 27 | uint8_t SR_1; 28 | 29 | uint8_t freq_lo_2; 30 | uint8_t freq_hi_2; 31 | uint8_t pw_lo_2; 32 | uint8_t pw_hi_2; 33 | uint8_t ctrl_2; 34 | uint8_t AD_2; 35 | uint8_t SR_2; 36 | 37 | uint8_t freq_lo_3; 38 | uint8_t freq_hi_3; 39 | uint8_t pw_lo_3; 40 | uint8_t pw_hi_3; 41 | uint8_t ctrl_3; 42 | uint8_t AD_3; 43 | uint8_t SR_3; 44 | 45 | uint8_t fc_lo; 46 | uint8_t fc_hi; 47 | uint8_t res_filt; 48 | uint8_t mode_vol; 49 | 50 | uint8_t pot_x; 51 | uint8_t pot_y; 52 | uint8_t osc_3; 53 | uint8_t env_3; 54 | }; 55 | 56 | 57 | // SID waveforms (some of them :-) 58 | enum { 59 | WAVE_NONE, 60 | WAVE_TRI, 61 | WAVE_SAW, 62 | WAVE_TRISAW, 63 | WAVE_RECT, 64 | WAVE_TRIRECT, 65 | WAVE_SAWRECT, 66 | WAVE_TRISAWRECT, 67 | WAVE_NOISE 68 | }; 69 | 70 | // EG states 71 | enum { 72 | EG_IDLE, 73 | EG_ATTACK, 74 | EG_DECAY, 75 | EG_RELEASE 76 | }; 77 | 78 | // Filter types 79 | enum { 80 | FILT_NONE, 81 | FILT_LP, 82 | FILT_BP, 83 | FILT_LPBP, 84 | FILT_HP, 85 | FILT_NOTCH, 86 | FILT_HPBP, 87 | FILT_ALL 88 | }; 89 | 90 | // Structure for one voice 91 | struct DRVoice { 92 | int wave; // Selected waveform 93 | int eg_state; // Current state of EG 94 | DRVoice *mod_by; // Voice that modulates this one 95 | DRVoice *mod_to; // Voice that is modulated by this one 96 | 97 | uint32_t count; // Counter for waveform generator, 8.16 fixed 98 | uint32_t add; // Added to counter in every frame 99 | 100 | uint16_t freq; // SID frequency value 101 | uint16_t pw; // SID pulse-width value 102 | 103 | uint32_t a_add; // EG parameters 104 | uint32_t d_sub; 105 | uint32_t s_level; 106 | uint32_t r_sub; 107 | uint32_t eg_level; // Current EG level, 8.16 fixed 108 | 109 | uint32_t noise; // Last noise generator output value 110 | 111 | bool gate; // EG gate bit 112 | bool ring; // Ring modulation bit 113 | bool test; // Test bit 114 | bool filter; // Flag: Voice filtered 115 | 116 | // The following bit is set for the modulating 117 | // voice, not for the modulated one (as the SID bits) 118 | bool sync; // Sync modulation bit 119 | }; 120 | 121 | 122 | static bool ready; // Flag: Renderer has initialized and is ready 123 | static uint8_t volume; // Master volume 124 | static bool v3_mute; // Voice 3 muted 125 | 126 | static DRVoice voice[3]; // Data for 3 voices 127 | 128 | static uint8_t f_type; // Filter type 129 | static uint8_t f_freq; // SID filter frequency (upper 8 bits) 130 | static uint8_t f_res; // Filter resonance (0..15) 131 | static float f_ampl; // IIR filter input attenuation 132 | static float d1, d2, g1, g2; // IIR filter coefficients 133 | static float xn1, xn2, yn1, yn2; // IIR filter previous input/output signal 134 | 135 | static uint8_t sample_buf[SAMPLE_BUF_SIZE]; // Buffer for sampled voice 136 | static int sample_in_ptr; // Index in sample_buf for writing 137 | 138 | static uint8_t regs[32]; // Copies of the 25 write-only SID registers 139 | static uint8_t last_sid_byte; // Last value written to SID 140 | 141 | static void init6581(); 142 | static void reset6581(void); 143 | 144 | static uint8_t read_register_6581(uint16_t adr); 145 | static void write_register_6581(uint16_t adr, uint8_t byte); 146 | static void emulate_line_6581(void); 147 | 148 | static void calc_filter(void); 149 | static void calc_buffer(int16_t *buf, long count); 150 | 151 | 152 | static uint16_t TriTable[0x1000*2]; 153 | 154 | #ifndef EMUL_MOS8580 155 | // Sampled from a 6581R4 156 | static const uint16_t TriSawTable[0x100] = { 157 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 158 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 159 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 160 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 161 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 162 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 163 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 164 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0808, 165 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 166 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 167 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 168 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 169 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 170 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 171 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 172 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1010, 0x3C3C, 173 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 174 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 175 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 176 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 177 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 178 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 179 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 180 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0808, 181 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 182 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 183 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 184 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 185 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 186 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 187 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 188 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1010, 0x3C3C 189 | }; 190 | 191 | static const uint16_t TriRectTable[0x100] = { 192 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 193 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 194 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 195 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 196 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 197 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 198 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 199 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 200 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 201 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 202 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 203 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 204 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 205 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 206 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 0xC0C0, 207 | 0x0000, 0x8080, 0x8080, 0xE0E0, 0x8080, 0xE0E0, 0xF0F0, 0xFCFC, 208 | 0xFFFF, 0xFCFC, 0xFAFA, 0xF0F0, 0xF6F6, 0xE0E0, 0xE0E0, 0x8080, 209 | 0xEEEE, 0xE0E0, 0xE0E0, 0x8080, 0xC0C0, 0x0000, 0x0000, 0x0000, 210 | 0xDEDE, 0xC0C0, 0xC0C0, 0x0000, 0x8080, 0x0000, 0x0000, 0x0000, 211 | 0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 212 | 0xBEBE, 0x8080, 0x8080, 0x0000, 0x8080, 0x0000, 0x0000, 0x0000, 213 | 0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 214 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 215 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 216 | 0x7E7E, 0x4040, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 217 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 218 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 219 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 220 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 221 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 222 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 223 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 224 | }; 225 | 226 | static const uint16_t SawRectTable[0x100] = { 227 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 228 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 229 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 230 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 231 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 232 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 233 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 234 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 235 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 236 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 237 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 238 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 239 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 240 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 241 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 242 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7878, 243 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 244 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 245 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 246 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 247 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 248 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 249 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 250 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 251 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 252 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 253 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 254 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 255 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 256 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 257 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 258 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7878 259 | }; 260 | 261 | static const uint16_t TriSawRectTable[0x100] = { 262 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 263 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 264 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 265 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 266 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 267 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 268 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 269 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 270 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 271 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 272 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 273 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 274 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 275 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 276 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 277 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 278 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 279 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 280 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 281 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 282 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 283 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 284 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 285 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 286 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 287 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 288 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 289 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 290 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 291 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 292 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 293 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 294 | }; 295 | #else 296 | // Sampled from an 8580R5 297 | static const uint16_t TriSawTable[0x100] = { 298 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 299 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 300 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 301 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 302 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 303 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 304 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 305 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0808, 306 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 307 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 308 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 309 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 310 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 311 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 312 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 313 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1818, 0x3C3C, 314 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 315 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 316 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 317 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 318 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 319 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 320 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 321 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1C1C, 322 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 323 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 324 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 325 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 326 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 327 | 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 0x0000, 0x8080, 0x8080, 328 | 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xE0E0, 329 | 0xF0F0, 0xF0F0, 0xF0F0, 0xF0F0, 0xF8F8, 0xF8F8, 0xFCFC, 0xFEFE 330 | }; 331 | 332 | static const uint16_t TriRectTable[0x100] = { 333 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 334 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 335 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 336 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 337 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 338 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 339 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 340 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 341 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 342 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 343 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 344 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 345 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 346 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 347 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 348 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 349 | 0xFFFF, 0xFCFC, 0xF8F8, 0xF0F0, 0xF4F4, 0xF0F0, 0xF0F0, 0xE0E0, 350 | 0xECEC, 0xE0E0, 0xE0E0, 0xC0C0, 0xE0E0, 0xC0C0, 0xC0C0, 0xC0C0, 351 | 0xDCDC, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0x8080, 0x8080, 352 | 0xC0C0, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x0000, 0x0000, 353 | 0xBEBE, 0xA0A0, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x0000, 354 | 0x8080, 0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 355 | 0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 356 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 357 | 0x7E7E, 0x7070, 0x6060, 0x0000, 0x4040, 0x0000, 0x0000, 0x0000, 358 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 359 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 360 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 361 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 362 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 363 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 364 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 365 | }; 366 | 367 | static const uint16_t SawRectTable[0x100] = { 368 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 369 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 370 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 371 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 372 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 373 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 374 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 375 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 376 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 377 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 378 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 379 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 380 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 381 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 382 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 383 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 384 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 385 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 386 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 387 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 388 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 389 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 390 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 0x8080, 391 | 0x0000, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0xB0B0, 0xBEBE, 392 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 393 | 0x0000, 0x0000, 0x0000, 0x8080, 0x8080, 0x8080, 0x8080, 0xC0C0, 394 | 0x0000, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0xC0C0, 395 | 0x8080, 0x8080, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xDCDC, 396 | 0x8080, 0x8080, 0x8080, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 397 | 0xC0C0, 0xC0C0, 0xC0C0, 0xE0E0, 0xE0E0, 0xE0E0, 0xE0E0, 0xECEC, 398 | 0xC0C0, 0xE0E0, 0xE0E0, 0xE0E0, 0xE0E0, 0xF0F0, 0xF0F0, 0xF4F4, 399 | 0xF0F0, 0xF0F0, 0xF8F8, 0xF8F8, 0xF8F8, 0xFCFC, 0xFEFE, 0xFFFF 400 | }; 401 | 402 | static const uint16_t TriSawRectTable[0x100] = { 403 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 404 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 405 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 406 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 407 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 408 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 409 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 410 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 411 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 412 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 413 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 414 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 415 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 416 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 417 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 418 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 419 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 420 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 421 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 422 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 423 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 424 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 425 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 426 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 427 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 428 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 429 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 430 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 431 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 432 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 0x8080, 433 | 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0xC0C0, 0xC0C0, 434 | 0xC0C0, 0xC0C0, 0xE0E0, 0xE0E0, 0xE0E0, 0xF0F0, 0xF8F8, 0xFCFC 435 | }; 436 | #endif 437 | 438 | static const uint32_t EGTable[16] = { 439 | (SID_CYCLES << 16) / 9, (SID_CYCLES << 16) / 32, 440 | (SID_CYCLES << 16) / 63, (SID_CYCLES << 16) / 95, 441 | (SID_CYCLES << 16) / 149, (SID_CYCLES << 16) / 220, 442 | (SID_CYCLES << 16) / 267, (SID_CYCLES << 16) / 313, 443 | (SID_CYCLES << 16) / 392, (SID_CYCLES << 16) / 977, 444 | (SID_CYCLES << 16) / 1954, (SID_CYCLES << 16) / 3126, 445 | (SID_CYCLES << 16) / 3906, (SID_CYCLES << 16) / 11720, 446 | (SID_CYCLES << 16) / 19531, (SID_CYCLES << 16) / 31251 447 | }; 448 | 449 | static const uint8_t EGDRShift[256] = { 450 | 5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4, 451 | 3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2, 452 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 453 | 2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1, 454 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 455 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 456 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 457 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 458 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 459 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 460 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 461 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 462 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 463 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 464 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 465 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 466 | }; 467 | 468 | static const int16_t SampleTab[16] = { 469 | (int16_t)0x8000, (int16_t)0x9111, (int16_t)0xa222, (int16_t)0xb333, 470 | (int16_t)0xc444, (int16_t)0xd555, (int16_t)0xe666, (int16_t)0xf777, 471 | (int16_t)0x0888, (int16_t)0x1999, (int16_t)0x2aaa, (int16_t)0x3bbb, 472 | (int16_t)0x4ccc, (int16_t)0x5ddd, (int16_t)0x6eee, (int16_t)0x7fff 473 | }; 474 | 475 | 476 | -------------------------------------------------------------------------------- /pi/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += -O3 -Wall -Wextra -I. $(shell sdl2-config --cflags) 2 | LIBS += $(shell sdl2-config --libs) 3 | DEPS = 6502.cpp 6569.cpp 6569.h 6581.cpp 6581.h display.cpp display.h sound.cpp fake_pi.cpp pi.cpp pi.h 4 | OBJ = main.o 5 | RM := rm -f 6 | 7 | %.o: %.cpp $(DEPS) 8 | $(CXX) -c -o $@ $< $(CFLAGS) 9 | 10 | emulator: $(OBJ) 11 | $(CXX) -o $@ $^ $(CFLAGS) $(LIBS) 12 | 13 | all: test 14 | 15 | .PHONY: clean 16 | clean: 17 | $(RM) $(OBJ) 18 | $(RM) emulator 19 | 20 | -------------------------------------------------------------------------------- /pi/c64_pi_dump.tar.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KimJorgensen/C64_Pi_Interface/b0c960458a04053751bff722c0359092d4143f57/pi/c64_pi_dump.tar.xz -------------------------------------------------------------------------------- /pi/char.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KimJorgensen/C64_Pi_Interface/b0c960458a04053751bff722c0359092d4143f57/pi/char.rom -------------------------------------------------------------------------------- /pi/display.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Based on 3 | * Display.cpp - C64 graphics display, emulator window handling 4 | * Display_SDL.i - C64 graphics display, emulator window handling, 5 | * SDL specific stuff 6 | * 7 | * Frodo (C) 1994-1997,2002 Christian Bauer 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | // Display surface 16 | static SDL_Surface *screen = NULL; 17 | static SDL_Renderer *renderer = NULL; 18 | static SDL_Texture *texture = NULL; 19 | 20 | #ifdef FAKE_PI 21 | static struct timeval tv_start; 22 | static double speed_index; 23 | static char speedometer_string[16]; // Speedometer text 24 | #endif 25 | 26 | 27 | /* 28 | * Open window 29 | */ 30 | 31 | static int display_init(void) 32 | { 33 | // Init SDL 34 | if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) != 0) 35 | { 36 | fprintf(stderr, "Couldn't initialize SDL (%s)\n", SDL_GetError()); 37 | return 0; 38 | } 39 | 40 | SDL_ShowCursor(SDL_DISABLE); 41 | screen = SDL_CreateRGBSurface(0, DISPLAY_X, DISPLAY_Y, 32, 42 | 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); 43 | 44 | display_bitmap_base = (uint32_t *)screen->pixels; 45 | display_bitmap_xmod = screen->pitch/sizeof(uint32_t); 46 | 47 | // Open window 48 | SDL_Window *window = SDL_CreateWindow("C64 - Pi", 49 | SDL_WINDOWPOS_UNDEFINED, 50 | SDL_WINDOWPOS_UNDEFINED, 51 | DISPLAY_X * 2, DISPLAY_Y * 2, 52 | SDL_WINDOW_RESIZABLE); 53 | 54 | renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); 55 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); 56 | SDL_RenderSetLogicalSize(renderer, DISPLAY_X, DISPLAY_Y); 57 | 58 | texture = SDL_CreateTexture(renderer, 59 | SDL_PIXELFORMAT_RGBA8888, 60 | SDL_TEXTUREACCESS_STREAMING, 61 | DISPLAY_X, DISPLAY_Y); 62 | display_update(); 63 | 64 | return 1; 65 | } 66 | 67 | 68 | /* 69 | * Close window 70 | */ 71 | 72 | static void display_close(void) 73 | { 74 | SDL_Quit(); 75 | } 76 | 77 | 78 | /* 79 | * Redraw bitmap 80 | */ 81 | 82 | static void display_update(void) 83 | { 84 | SDL_UpdateTexture(texture, NULL, screen->pixels, screen->pitch); 85 | 86 | SDL_RenderClear(renderer); 87 | SDL_RenderCopy(renderer, texture, NULL, NULL); 88 | SDL_RenderPresent(renderer); 89 | } 90 | 91 | 92 | /* 93 | * Draw string into surface using the C64 ROM font 94 | */ 95 | 96 | static void display_draw_string(int x, int y, const char *str, uint32_t front_color, uint32_t back_color) 97 | { 98 | SDL_Surface *s = screen; 99 | uint32_t *pb = (uint32_t *)s->pixels + s->pitch*y/sizeof(uint32_t) + x; 100 | char c; 101 | while ((c = *str++) != 0) { 102 | uint8_t *q = char_rom + c*8 + 0x800; 103 | uint32_t *p = pb; 104 | for (int y=0; y<8; y++) { 105 | uint8_t v = *q++; 106 | p[0] = (v & 0x80) ? front_color : back_color; 107 | p[1] = (v & 0x40) ? front_color : back_color; 108 | p[2] = (v & 0x20) ? front_color : back_color; 109 | p[3] = (v & 0x10) ? front_color : back_color; 110 | p[4] = (v & 0x08) ? front_color : back_color; 111 | p[5] = (v & 0x04) ? front_color : back_color; 112 | p[6] = (v & 0x02) ? front_color : back_color; 113 | p[7] = (v & 0x01) ? front_color : back_color; 114 | p += s->pitch/sizeof(uint32_t); 115 | } 116 | pb += 8; 117 | } 118 | } 119 | 120 | static void display_poll_keyboard() 121 | { 122 | SDL_Event event; 123 | while (SDL_PollEvent(&event)) 124 | { 125 | switch (event.type) 126 | { 127 | // Key pressed 128 | case SDL_KEYDOWN: 129 | switch (event.key.keysym.sym) 130 | { 131 | case SDLK_F4: // F4: Quit 132 | quit_requested = true; 133 | break; 134 | 135 | case SDLK_KP_PLUS: // '+' on keypad: turbo mode (debug) 136 | debug_turbo = !debug_turbo; 137 | break; 138 | 139 | default: 140 | break; 141 | } 142 | break; 143 | 144 | case SDL_QUIT: 145 | quit_requested = true; 146 | break; 147 | 148 | default: 149 | break; 150 | } 151 | } 152 | } 153 | 154 | static void vic_vblank() 155 | { 156 | display_poll_keyboard(); 157 | 158 | #ifdef FAKE_PI 159 | display_draw_string(0, DISPLAY_Y - 8, speedometer_string, palette[6], palette[0]); 160 | #endif 161 | 162 | display_update(); 163 | 164 | #ifdef FAKE_PI 165 | // Calculate time between vblanks, display speedometer 166 | struct timeval tv; 167 | gettimeofday(&tv, NULL); 168 | if ((tv.tv_usec -= tv_start.tv_usec) < 0) { 169 | tv.tv_usec += 1000000; 170 | tv.tv_sec -= 1; 171 | } 172 | tv.tv_sec -= tv_start.tv_sec; 173 | double elapsed_time = (double)tv.tv_sec * 1000000 + tv.tv_usec; 174 | speed_index = 20000 / (elapsed_time + 1) * 100; 175 | 176 | // Limit speed to 100% 177 | if (!debug_turbo && speed_index > 100) { 178 | usleep((unsigned long)(20000 - elapsed_time)); 179 | speed_index = 100; 180 | } 181 | 182 | gettimeofday(&tv_start, NULL); 183 | 184 | static int delay = 0; 185 | if (delay >= 20) 186 | { 187 | delay = 0; 188 | sprintf(speedometer_string, "%d%%", (int)speed_index); 189 | } 190 | else 191 | { 192 | delay++; 193 | } 194 | #endif 195 | } 196 | 197 | -------------------------------------------------------------------------------- /pi/display.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Based on Display.h - C64 graphics display, emulator window handling 3 | * 4 | * Frodo (C) 1994-1997,2002 Christian Bauer 5 | */ 6 | 7 | // Display dimensions 8 | static const int DISPLAY_X = 0x180; 9 | static const int DISPLAY_Y = 0x110; 10 | 11 | // C64 color palette in RGBA (from https://www.pepto.de/projects/colorvic/) 12 | static const uint32_t palette[16] = 13 | { 14 | 0x000000ff, 0xffffffff, 0x813338ff, 0x75cec8ff, 15 | 0x8e3c97ff, 0x56ac4dff, 0x2e2c9bff, 0xedf171ff, 16 | 0x8e5029ff, 0x553800ff, 0xc46c71ff, 0x4a4a4aff, 17 | 0x7b7b7bff, 0xa9ff9fff, 0x706debff, 0xb2b2b2ff 18 | }; 19 | 20 | static uint32_t *display_bitmap_base; // Pointer to bitmap data 21 | static int display_bitmap_xmod; // Number of bytes per row 22 | 23 | static void display_update(void); 24 | static void display_draw_string(int x, int y, const char *str, uint32_t front_color, uint32_t back_color); 25 | 26 | static void vic_vblank(); 27 | 28 | -------------------------------------------------------------------------------- /pi/fake_pi.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Emulate Raspberry Pi specific code 3 | // 4 | 5 | static const uint32_t chunck_size = 4096; 6 | 7 | uint32_t buf_len = 256*1024*1024; 8 | uint32_t buf_ptr; 9 | uint16_t *stream_buf; 10 | 11 | static void cleanup_smi() 12 | { 13 | free(stream_buf); 14 | } 15 | 16 | static void cleanup_smi_and_exit(int sig) 17 | { 18 | printf("\nExiting with error; caught signal: %i\n", sig); 19 | cleanup_smi(); 20 | exit(1); 21 | } 22 | 23 | static bool start_smi_dma() 24 | { 25 | stream_buf = (uint16_t *)malloc(buf_len); 26 | if (stream_buf == 0) 27 | { 28 | fprintf(stderr, "Buffer malloc failed\n"); 29 | return false; 30 | } 31 | 32 | FILE *file; 33 | file = fopen("c64_pi_dump.bin", "r"); 34 | if (file != NULL) 35 | { 36 | if (fread(stream_buf, buf_len, 1, file) != 1) 37 | { 38 | fprintf(stderr, "Failed to read data from file\n"); 39 | return false; 40 | } 41 | fclose(file); 42 | } 43 | else 44 | { 45 | fprintf(stderr, "Failed to open c64_pi_dump.bin for reading. %s\n", 46 | strerror(errno)); 47 | return false; 48 | } 49 | 50 | buf_ptr = 0; 51 | return true; 52 | } 53 | 54 | static uint16_t *get_next_smi_chunk() 55 | { 56 | uint16_t *result; 57 | 58 | if (buf_ptr < buf_len/sizeof(uint16_t)) 59 | { 60 | result = stream_buf + buf_ptr; 61 | buf_ptr += chunck_size/sizeof(uint16_t); 62 | } 63 | else 64 | { 65 | printf("End of stream\n"); 66 | cleanup_smi(); 67 | exit(0); 68 | } 69 | 70 | return result; 71 | } 72 | -------------------------------------------------------------------------------- /pi/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "display.h" 10 | #include "6581.h" 11 | #include "pi.h" 12 | 13 | #include "6502.cpp" 14 | 15 | #undef FAKE_PI 16 | //#define FAKE_PI 17 | 18 | // CPU cycle counter 19 | static uint32_t cycle_counter; 20 | 21 | static uint8_t ram[0x10000]; 22 | static uint8_t color_ram[0x0400]; 23 | static uint8_t char_rom[0x1000]; 24 | 25 | // VIC-II stuff 26 | static bool vic_ba_Low; 27 | static bool vic_in_sync = false; 28 | 29 | static void vic_trigger_irq() 30 | { 31 | // Ignore 32 | } 33 | 34 | static void vic_clear_irq() 35 | { 36 | // Ignore 37 | } 38 | 39 | static bool quit_requested = false; 40 | static bool debug_turbo = true; 41 | 42 | #include "6569.cpp" 43 | #include "sound.cpp" 44 | #include "6581.cpp" 45 | #include "display.cpp" 46 | 47 | #ifndef FAKE_PI 48 | #include "pi.cpp" 49 | #else 50 | #include "fake_pi.cpp" 51 | #endif 52 | 53 | static uint8_t ddr_6510 = 0x00; 54 | static uint8_t dr_6510 = 0x3f; 55 | static bool io_visible = false; 56 | 57 | inline static void cpu_changed_port() 58 | { 59 | uint8_t port = ~ddr_6510 | dr_6510; 60 | io_visible = (port & 0x03) && (port & 0x04); 61 | } 62 | 63 | static uint8_t pra_6526_2 = 0x00; 64 | static uint8_t ddra_6526_2 = 0x00; 65 | 66 | inline static void cia2_changed_va() 67 | { 68 | // VA14/15 69 | changed_va_6569(~(pra_6526_2 | ~ddra_6526_2) & 3); 70 | } 71 | 72 | static void reset6526_2() 73 | { 74 | pra_6526_2 = 0; 75 | ddra_6526_2 = 0; 76 | cia2_changed_va(); 77 | } 78 | 79 | static void reset_sim() 80 | { 81 | cycle_counter = -1; 82 | ddr_6510 = 0x00; 83 | dr_6510 = 0x3f; 84 | cpu_changed_port(); 85 | reset6502(); 86 | 87 | reset6526_2(); 88 | reset6569(); 89 | reset6581(); 90 | } 91 | 92 | static void write_register_6526_2(uint8_t address, uint8_t data) 93 | { 94 | switch (address) 95 | { 96 | case 0x0: 97 | pra_6526_2 = data; 98 | cia2_changed_va(); 99 | break; 100 | case 0x2: 101 | ddra_6526_2 = data; 102 | cia2_changed_va(); 103 | break; 104 | } 105 | } 106 | 107 | static void mem_write(uint16_t address, uint8_t data) 108 | { 109 | if (address < 0xd000 || address >= 0xe000 || !io_visible) 110 | { 111 | ram[address] = data; 112 | } 113 | else 114 | { 115 | switch ((address >> 8) & 0x0f) 116 | { 117 | case 0x0: // VIC 118 | case 0x1: 119 | case 0x2: 120 | case 0x3: 121 | write_register_6569(address & 0x3f, data); 122 | break; 123 | case 0x4: // SID 124 | case 0x5: 125 | case 0x6: 126 | case 0x7: 127 | write_register_6581(address & 0x1f, data); 128 | break; 129 | case 0x8: // Color RAM 130 | case 0x9: 131 | case 0xa: 132 | case 0xb: 133 | color_ram[address & 0x03ff] = data & 0x0f; 134 | break; 135 | case 0xc: // CIA 1 136 | break; 137 | case 0xd: // CIA 2 138 | write_register_6526_2(address & 0x0f, data); 139 | break; 140 | case 0xe: // I/O 1 141 | break; 142 | case 0xf: // I/O 2 143 | break; 144 | } 145 | } 146 | } 147 | 148 | struct smi_stream 149 | { 150 | uint16_t *chunk_at; 151 | uint32_t remaining; 152 | uint16_t *next_chunk; 153 | }; 154 | 155 | static void get_next_chunk(smi_stream *stream) 156 | { 157 | if (stream->next_chunk) 158 | { 159 | stream->chunk_at = stream->next_chunk; 160 | stream->next_chunk = 0; 161 | } 162 | else 163 | { 164 | stream->chunk_at = get_next_smi_chunk(); 165 | } 166 | 167 | stream->remaining = chunck_size/sizeof(uint16_t); 168 | } 169 | 170 | static uint16_t get(smi_stream *stream) 171 | { 172 | uint16_t result = *stream->chunk_at++; 173 | 174 | if (--stream->remaining == 0) 175 | { 176 | get_next_chunk(stream); 177 | } 178 | 179 | return result; 180 | } 181 | 182 | static uint16_t peek(smi_stream *stream, uint32_t pos) 183 | { 184 | uint16_t result; 185 | 186 | if (pos >= stream->remaining) 187 | { 188 | pos -= stream->remaining; 189 | if (!stream->next_chunk) 190 | { 191 | stream->next_chunk = get_next_smi_chunk(); 192 | } 193 | 194 | result = stream->next_chunk[pos]; 195 | } 196 | else 197 | { 198 | result = stream->chunk_at[pos]; 199 | } 200 | 201 | return result; 202 | } 203 | 204 | int main() 205 | { 206 | // Catch all signals (like ctrl+c, ctrl+z, ...) to ensure DMA is disabled 207 | for (int i = 0; i < 64; i++) 208 | { 209 | struct sigaction sa; 210 | memset(&sa, 0, sizeof(sa)); 211 | sa.sa_handler = cleanup_smi_and_exit; 212 | sigaction(i, &sa, NULL); 213 | } 214 | 215 | id_t pid = getpid(); 216 | setpriority(PRIO_PROCESS, pid, -20); 217 | 218 | FILE *rom_file = fopen("char.rom", "r"); 219 | if (rom_file != NULL) 220 | { 221 | if (fread(char_rom, sizeof(char_rom), 1, rom_file) != 1) 222 | { 223 | fprintf(stderr, "Failed to read data from char.rom file\n"); 224 | return 1; 225 | } 226 | fclose(rom_file); 227 | } 228 | else 229 | { 230 | fprintf(stderr, "Failed to open char.rom file for reading\n%s\n", 231 | strerror(errno)); 232 | return 1; 233 | } 234 | 235 | if (!display_init()) 236 | { 237 | fprintf(stderr, "Failed to init display\n"); 238 | return 1; 239 | } 240 | 241 | init6581(); 242 | sound_init(); 243 | 244 | reset_sim(); 245 | bool interrupt = false; 246 | 247 | if (!start_smi_dma()) 248 | { 249 | sound_close(); 250 | display_close(); 251 | return 1; 252 | } 253 | 254 | display_draw_string(96, 120, "WAITING FOR C64 TO RESET", palette[14], palette[0]); 255 | display_update(); 256 | 257 | smi_stream stream = {}; 258 | get_next_chunk(&stream); 259 | 260 | // Wait for reset 261 | while (peek(&stream, 0) != 0xFFFC || peek(&stream, 2) != 0xFFFD) 262 | { 263 | get(&stream); 264 | } 265 | 266 | for (cycle_counter=0;; cycle_counter++) 267 | { 268 | uint16_t address = get(&stream); 269 | uint16_t status_data = get(&stream); 270 | 271 | uint8_t status = (uint8_t)(status_data >> 8); 272 | uint8_t data = (uint8_t)status_data; 273 | 274 | if (status & 0xFC) 275 | { 276 | fprintf(stderr, "Invalid status byte %02x at %d\n", status, cycle_counter); 277 | break; 278 | } 279 | 280 | bool ba = status & 0x02; 281 | bool write = status & 0x01; 282 | 283 | // Handle IRQ/NMI 284 | if (interrupt) 285 | { 286 | if (cpu.cycle == 1) 287 | { 288 | interrupt = false; 289 | } 290 | else if (cpu.cycle == 4) 291 | { 292 | cpu.data &= ~FLAG_BREAK; 293 | } 294 | else if (cpu.cycle == 5) 295 | { 296 | if (address == 0xfffa) // Check for NMI 297 | { 298 | cpu.addr = address; 299 | } 300 | } 301 | } 302 | 303 | // Look ahead to detect interrupt - 304 | // 3 consecutive writes only occur during an interrupt or BRK 305 | if (!interrupt && cpu.cycle == 1 && (peek(&stream, 1) & 0x0100) && 306 | (peek(&stream, 3) & 0x0100) && (peek(&stream, 5) & 0x0100)) 307 | { 308 | if (cpu.opcode != 0x00) // Check for BRK instruction 309 | { 310 | cpu.opcode = 0x00; 311 | cpu.addr = --cpu.pc; 312 | --cpu.pc; 313 | interrupt = true; 314 | } 315 | } 316 | 317 | // TODO: Improve the VIC-II sync 318 | if (vic_in_sync || !vic_ba_Low) 319 | { 320 | if (emulate_cycle_6569()) 321 | { 322 | emulate_line_6581(); 323 | } 324 | } 325 | else if (ba) 326 | { 327 | // The VIC-II emulator is in sync with the real C64 328 | vic_in_sync = true; 329 | } 330 | 331 | if (quit_requested) 332 | { 333 | printf("Quit requested (%d)\n", cycle_counter); 334 | break; 335 | } 336 | 337 | // Skip VIC-II cycle 338 | if (!ba || write) 339 | { 340 | // TODO: Fix this "+ 0x0100" hack. The 6502 seems to do calc during the VIC-II cycle 341 | if (address != cpu.addr && address != (uint16_t)(cpu.addr + 0x0100)) 342 | { 343 | printf("%c %04x %02x - op: %02x%c(%u) - %d\n", write ? 'W' : 'R', 344 | address, data, cpu.opcode, interrupt ? '*' : ' ', cpu.cycle, cycle_counter); 345 | fprintf(stderr, "Unexpected address: %04x. Expected: %04x (%u)\n", 346 | address, cpu.addr, cycle_counter); 347 | 348 | // TODO: Try to recover 349 | break; 350 | } 351 | 352 | if (write) 353 | { 354 | if (!cpu.write) 355 | { 356 | fprintf(stderr, "Unexpected write. Expected read\n"); 357 | // TODO: Try to recover 358 | break; 359 | } 360 | 361 | if (address == 0x0000) 362 | { 363 | ddr_6510 = cpu.data & 0x3f; 364 | cpu_changed_port(); 365 | } 366 | else if (address == 0x0001) 367 | { 368 | dr_6510 = cpu.data & 0x3f; 369 | cpu_changed_port(); 370 | } 371 | // Ignore upper nibble (which is "random" when read from color RAM) 372 | else if ((data & 0x0f) != (cpu.data & 0x0f)) 373 | { 374 | fprintf(stderr, "Unexpected data to write at %04x: %02x. Expected: %02x\n", 375 | address, data, cpu.data); 376 | // TODO: Try to recover 377 | break; 378 | } 379 | 380 | mem_write(address, data); 381 | } 382 | else 383 | { 384 | if (cpu.write) 385 | { 386 | fprintf(stderr, "Unexpected read. Expected write\n"); 387 | // TODO: Try to recover 388 | break; 389 | } 390 | 391 | if (address == 0x0000) 392 | { 393 | cpu.data = ddr_6510; 394 | } 395 | else if (address == 0x0001) 396 | { 397 | // Assume sense is always high (no tape button pressed) 398 | cpu.data = dr_6510 | 0x10; 399 | } 400 | else 401 | { 402 | cpu.data = data; 403 | } 404 | } 405 | 406 | step6502(); 407 | } 408 | } 409 | 410 | cleanup_smi(); 411 | sound_close(); 412 | display_close(); 413 | 414 | return 0; 415 | } 416 | -------------------------------------------------------------------------------- /pi/pi.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Raspberry Pi specific code 3 | // Based on 4 | // https://github.com/fenlogic/IDE_trial 5 | // https://github.com/Wallacoloo/Raspberry-Pi-DMA-Example 6 | // https://github.com/juj/fbcp-ili9341 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define PAGE_SIZE (4*1024) 16 | #define BLOCK_SIZE (4*1024) 17 | 18 | static void cleanup_smi(); 19 | 20 | // Highjack DMA channel 1 (should be free) 21 | static const uint8_t dma_ch = 1; 22 | 23 | static int mem_fd = -1; 24 | static int vcio_fd = -1; 25 | 26 | // I/O access 27 | static volatile uint32_t *dma = 0; 28 | static volatile uint32_t *clk = 0; 29 | static volatile uint32_t *gpio = 0; 30 | static volatile uint32_t *smi = 0; 31 | 32 | // DMA control block needs to be on 32 byte boundary 33 | typedef struct 34 | { 35 | uint32_t info; 36 | uint32_t src; 37 | uint32_t dst; 38 | uint32_t length; 39 | uint32_t stride; 40 | uint32_t next; 41 | uint32_t pad[2]; // to fill alignment 42 | } dma_cb_type; 43 | 44 | typedef struct 45 | { 46 | uint32_t handle; 47 | volatile uint8_t *virt_addr; 48 | uintptr_t bus_addr; 49 | size_t bytes; 50 | } gpu_memory; 51 | 52 | static gpu_memory dma_buffer = {}; 53 | 54 | static const uint32_t chunck_size = 32760; // 130 raster lines (PAL) 55 | static uint16_t *chunk_virt_addr[4]; 56 | static uint32_t dma_cb_bus_addr[5]; 57 | 58 | static uint32_t cb_index, current_cb; 59 | 60 | // 61 | // Map the physical address of a peripheral into virtual address space. 62 | // 63 | static volatile uint32_t *map_to_virt(size_t length, off_t addr) 64 | { 65 | void *mapped = mmap( 66 | NULL, 67 | length, 68 | PROT_READ | PROT_WRITE, 69 | MAP_SHARED, 70 | mem_fd, 71 | addr); 72 | 73 | if (mapped == MAP_FAILED) 74 | { 75 | fprintf(stderr, "Could not mmap address %p\n", (void *)addr); 76 | exit(-1); 77 | } 78 | 79 | return (volatile uint32_t *)mapped; 80 | } 81 | 82 | static uint32_t send_mailbox(uint32_t tag, uint32_t num_args, ...) 83 | { 84 | uint32_t mailbox[9], i; 85 | va_list args; 86 | int ret; 87 | 88 | if (num_args > 3) 89 | { 90 | fprintf(stderr, "Too many args for send_mailbox %u\n", num_args); 91 | exit(1); 92 | } 93 | 94 | mailbox[MAILBOX_BUFFER_SIZE] = sizeof(mailbox); 95 | mailbox[MAILBOX_RE_CODE] = 0; 96 | mailbox[MAILBOX_TAG_ID] = tag; 97 | mailbox[MAILBOX_TAG_BUF_SIZE] = num_args*sizeof(uint32_t); 98 | 99 | va_start(args, num_args); 100 | for (i=0; ivirt_addr) 146 | { 147 | munmap((void *)memory->virt_addr, memory->bytes); 148 | memory->virt_addr = 0; 149 | } 150 | 151 | if (memory->bus_addr) 152 | { 153 | status = send_mailbox(TAG_UNLOCK_MEMORY, 1, memory->handle); 154 | if (!status) 155 | { 156 | memory->bus_addr = 0; 157 | } 158 | else 159 | { 160 | fprintf(stderr, "Failed to unlock GPU memory\n"); 161 | } 162 | } 163 | 164 | if (memory->handle) 165 | { 166 | status = send_mailbox(TAG_RELEASE_MEMORY, 1, memory->handle); 167 | if (!status) 168 | { 169 | memory->handle = 0; 170 | } 171 | else 172 | { 173 | fprintf(stderr, "Failed to release GPU memory\n"); 174 | } 175 | } 176 | } 177 | } 178 | 179 | // Allocates the given number of bytes in GPU side memory, and returns the 180 | // virtual address and physical bus address of the allocated memory block. 181 | // The virtual address holds an uncached view to the allocated memory, so 182 | // writes and reads to that memory address bypass the L1 and L2 caches. Use 183 | // this kind of memory to pass data blocks over to the DMA controller to process. 184 | static gpu_memory alloc_gpu_mem(size_t bytes) 185 | { 186 | gpu_memory result = {}; 187 | 188 | result.handle = send_mailbox(TAG_ALLOCATE_MEMORY, 3, 189 | bytes, PAGE_SIZE, MEM_FLAG_L1_NONALLOCATING | MEM_FLAG_ZERO); 190 | if (result.handle) 191 | { 192 | result.bus_addr = send_mailbox(TAG_LOCK_MEMORY, 1, result.handle); 193 | if (result.bus_addr) 194 | { 195 | result.virt_addr = (volatile uint8_t *)map_to_virt(bytes, 196 | BUS_TO_PHYS(result.bus_addr)); 197 | 198 | result.bytes = bytes; 199 | } 200 | else 201 | { 202 | fprintf(stderr, "Failed to lock GPU memory\n"); 203 | free_gpu_mem(&result); 204 | } 205 | } 206 | else 207 | { 208 | fprintf(stderr, "Failed to allocate GPU memory\n"); 209 | } 210 | 211 | return result; 212 | } 213 | 214 | // 215 | // Set up memory regions to access the peripherals. 216 | // 217 | static bool setup_io() 218 | { 219 | if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC)) < 0) 220 | { 221 | fprintf(stderr, "Can't open /dev/mem\n"); 222 | fprintf(stderr, "Did you forget to run as root?\n"); 223 | return false; 224 | } 225 | 226 | if ((vcio_fd = open("/dev/vcio", O_RDWR|O_SYNC)) < 0) 227 | { 228 | fprintf(stderr, "Can't open /dev/vcio\n"); 229 | return false; 230 | } 231 | 232 | dma = map_to_virt(BLOCK_SIZE, DMA_BASE); 233 | clk = map_to_virt(BLOCK_SIZE, CLOCK_BASE); 234 | gpio = map_to_virt(BLOCK_SIZE, GPIO_BASE); 235 | smi = map_to_virt(BLOCK_SIZE, SMI_BASE); 236 | 237 | dma_buffer = alloc_gpu_mem(5*sizeof(dma_cb_type) + 4*chunck_size); 238 | if (dma_buffer.virt_addr == 0) 239 | { 240 | fprintf(stderr, "Alloc of DMA buffer failed\n"); 241 | return false; 242 | } 243 | 244 | return true; 245 | } 246 | 247 | // 248 | // Undo what we did above. 249 | // 250 | static void restore_io() 251 | { 252 | if (smi) 253 | { 254 | munmap((void *)dma, BLOCK_SIZE); 255 | munmap((void *)clk, BLOCK_SIZE); 256 | munmap((void *)gpio, BLOCK_SIZE); 257 | munmap((void *)smi, BLOCK_SIZE); 258 | } 259 | 260 | free_gpu_mem(&dma_buffer); 261 | close(mem_fd); 262 | close(vcio_fd); 263 | } 264 | 265 | // 266 | // Set up basic SMI mode 267 | // 268 | static void setup_smi() 269 | { 270 | // Set GPIO 0..25 to SMI mode 271 | for (int i=0; i<26; i++) 272 | { 273 | INP_GPIO(i); 274 | SET_GPIO_ALT(i, 1); 275 | } 276 | 277 | // Disable SMI DMA 278 | smi[SMI_DMAC_REG] = 0; 279 | 280 | // Clear any errors and disable 281 | smi[SMI_CS_REG] = SMI_CS_AFERR|SMI_CS_SETERR|SMI_CS_FFCLR|SMI_CS_DONE; 282 | smi[SMI_DIRCS_REG] = SMI_DIRCS_DONE; 283 | 284 | // Set SMI clock 285 | // Use PLL-D as that is not changing (500MHz) 286 | volatile uint32_t *smi_clk = clk + (0xB0>>2); 287 | *smi_clk = 0x5A000000; // Off 288 | *(smi_clk+1) = 0x5A002000; // div 2 = 250MHz 289 | *smi_clk = 0x5A000016; // PLLD & enabled 290 | 291 | // Initially set all timing registers to 16 bit intel mode 292 | // assuming a 250MHz SMI clock 293 | volatile uint32_t *read_reg = smi + SMI_READ0_REG; 294 | for (int i=0; i<8; i++) 295 | { 296 | read_reg[i] = 297 | SMI_RW_WID16 | // 16 bits 298 | SMI_RW_MODE80 | // intel mode 299 | SMI_RW_PACEALL | // always pace 300 | SMI_RW_DREQ | // Use external DMA req on SD16/SD17 to pace reads/writes 301 | (8<info = 392 | (4<src = SMI_BASE_BUS + SMI_DATA_REG*sizeof(uint32_t); 397 | rx1_from_smi->dst = chunk_bus_addr1; 398 | rx1_from_smi->length = chunck_size; 399 | rx1_from_smi->next = dma_cb_bus_addr[1]; 400 | 401 | // DMA control block 1 - read chunk 2 402 | rx2_from_smi->info = 403 | (4<src = SMI_BASE_BUS + SMI_DATA_REG*sizeof(uint32_t); 408 | rx2_from_smi->dst = chunk_bus_addr2; 409 | rx2_from_smi->length = chunck_size; 410 | rx2_from_smi->next = dma_cb_bus_addr[2]; 411 | 412 | // DMA control block 2 - read chunk 3 413 | rx3_from_smi->info = 414 | (4<src = SMI_BASE_BUS + SMI_DATA_REG*sizeof(uint32_t); 419 | rx3_from_smi->dst = chunk_bus_addr3; 420 | rx3_from_smi->length = chunck_size; 421 | rx3_from_smi->next = dma_cb_bus_addr[3]; 422 | 423 | // DMA control block 3 - read chunk 4 424 | rx4_from_smi->info = 425 | (4<src = SMI_BASE_BUS + SMI_DATA_REG*sizeof(uint32_t); 430 | rx4_from_smi->dst = chunk_bus_addr4; 431 | rx4_from_smi->length = chunck_size; 432 | rx4_from_smi->next = dma_cb_bus_addr[4]; 433 | 434 | // DMA control block 4 - start SMI read 435 | smi_rx_start->info = 0; 436 | smi_rx_start->src = dma_cb_bus_addr[4] + 6*sizeof(uint32_t); // Point to pad[0] 437 | smi_rx_start->dst = SMI_BASE_BUS + SMI_CS_REG*sizeof(uint32_t); 438 | smi_rx_start->length = sizeof(uint32_t); 439 | smi_rx_start->pad[0] = SMI_CS_PXLDAT|SMI_CS_START|SMI_CS_ENABLE; 440 | smi_rx_start->next = dma_cb_bus_addr[0]; 441 | 442 | setup_smi_read(0xFFFFFFFF); 443 | //setup_smi_read(256); 444 | 445 | current_cb = dma_cb_bus_addr[0]; 446 | cb_index = 3; 447 | 448 | // DMA setup 449 | dma[DMA_CONBLK_AD_REG(dma_ch)] = dma_cb_bus_addr[4]; 450 | 451 | // Start DMA 452 | dma[DMA_CS_REG(dma_ch)] |= DMA_CS_ACTIVE; 453 | while ((dma[DMA_CS_REG(dma_ch)] & DMA_CS_ACTIVE) == 0); 454 | 455 | return true; 456 | } 457 | 458 | static uint16_t *get_next_smi_chunk() 459 | { 460 | cb_index++; 461 | if (cb_index > 3) 462 | { 463 | cb_index = 0; 464 | } 465 | 466 | if (dma_cb_bus_addr[cb_index] != current_cb) 467 | { 468 | return chunk_virt_addr[cb_index]; 469 | } 470 | 471 | // TODO: Is it possible to use interrupts in userspace when DMA CB is done? 472 | while (dma[DMA_CS_REG(dma_ch)] & DMA_CS_ACTIVE) 473 | { 474 | uint32_t new_cb = dma[DMA_CONBLK_AD_REG(dma_ch)]; 475 | if (new_cb != current_cb && new_cb != dma_cb_bus_addr[4]) 476 | { 477 | current_cb = new_cb; 478 | return chunk_virt_addr[cb_index]; 479 | } 480 | 481 | //usleep(1); 482 | } 483 | 484 | printf("DMA done\n"); 485 | cleanup_smi(); 486 | exit(1); 487 | } 488 | 489 | static void cleanup_smi() 490 | { 491 | // Disable DMA. Otherwise, it will continue to run in the background, 492 | // potentially overwriting future user data. 493 | if (dma) 494 | { 495 | dma[DMA_CS_REG(dma_ch)] = DMA_CS_RESET; 496 | while (dma[DMA_CS_REG(dma_ch)] & DMA_CS_ACTIVE); 497 | } 498 | 499 | if (smi) 500 | { 501 | setup_smi(); 502 | } 503 | 504 | restore_io(); 505 | } 506 | 507 | static void cleanup_smi_and_exit(int sig) 508 | { 509 | printf("\nExiting with error; caught signal: %i\n", sig); 510 | cleanup_smi(); 511 | exit(1); 512 | } 513 | 514 | -------------------------------------------------------------------------------- /pi/pi.h: -------------------------------------------------------------------------------- 1 | // 2 | // Raspberry Pi specific constants 3 | // Based on 4 | // https://github.com/fenlogic/IDE_trial 5 | // https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface 6 | // https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf 7 | // 8 | 9 | #if 0 10 | #define BCM2708_PERI_BASE 0x20000000 // For Pi1/Zero 11 | #else 12 | #define BCM2708_PERI_BASE 0x3F000000 // For Pi2/3 13 | #endif 14 | 15 | #define DMA_BASE (BCM2708_PERI_BASE + 0x007000) 16 | #define CLOCK_BASE (BCM2708_PERI_BASE + 0x101000) 17 | #define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) 18 | #define SMI_BASE (BCM2708_PERI_BASE + 0x600000) 19 | 20 | #define SMI_BASE_BUS 0x7E600000 21 | 22 | // 23 | // SMI 24 | // 25 | 26 | // Registers offset in CPU map 27 | // Assuming we access the register through a 4K MMU mapped address range 28 | // As such the register addresses are the offsets in that range 29 | //====== 30 | #define SMI_CS_REG 0 //** Control & Status register 31 | #define SMI_CS_RXFF_FULL 0x80000000 // RX FIFO full 32 | #define SMI_CS_TXFF_EMPTY 0x40000000 // TX FIFO not empty 33 | #define SMI_CS_RXFF_DATA 0x20000000 // RX FIFO holds data 34 | #define SMI_CS_TXFF_SPACE 0x10000000 // TX FIFO can accept data 35 | #define SMI_CS_RXFF_HIGH 0x08000000 // RX FIFO >= 3/4 full 36 | #define SMI_CS_TXFF_LOW 0x04000000 // TX FIFO < 1/4 full 37 | #define SMI_CS_AFERR 0x02000000 // AXI FIFO unsderflow/overflow 38 | // Write with bit set to clear 39 | #define SMI_CS_EDREQ 0x00008000 // External DREQ received 40 | #define SMI_CS_PXLDAT 0x00004000 // Pixel mode on 41 | #define SMI_CS_SETERR 0x00002000 // Setup reg. written & used error 42 | #define SMI_CS_PVMODE 0x00001000 // Pixel Valve mode on 43 | #define SMI_CS_RXIRQ 0x00000800 // Receive interrupt 44 | #define SMI_CS_TXIRQ 0x00000400 // Transmit interrupt 45 | #define SMI_CS_DONIRQ 0x00000200 // Done interrupt 46 | #define SMI_CS_TEEN 0x00000100 // Tear enable 47 | #define SMI_CS_BYTPAD 0x000000C0 // Number of PAD byte 48 | #define SMI_CS_WRITE 0x00000020 // 1= Write to ext. devices 49 | // 0= Read from ext. devices 50 | #define SMI_CS_FFCLR 0x00000010 // Write to 1 to clear FIFO 51 | #define SMI_CS_START 0x00000008 // Write to 1 to start transfer(s) 52 | #define SMI_CS_BUSY 0x00000004 // Set if transfer is taking place 53 | #define SMI_CS_DONE 0x00000002 // Set if transfer has finished 54 | // For reads only set after FF emptied 55 | // Write with bit set to clear 56 | #define SMI_CS_ENABLE 0x00000001 // Set to enable SMI 57 | 58 | #define SMI_LENGTH_REG 1 //** transfer length register 59 | 60 | #define SMI_ADRS_REG 2 //** transfer address register 61 | #define SMI_ADRS_DEV_MSK 0x00000300 // Which device timing to use 62 | #define SMI_ADRS_DEV_LS 8 63 | #define SMI_ADRS_DEV0 0x00000000 // Use read0/write0 timing 64 | #define SMI_ADRS_DEV1 0x00000100 // Use read1/write1 timing 65 | #define SMI_ADRS_DEV2 0x00000200 // Use read2/write2 timing 66 | #define SMI_ADRS_DEV3 0x00000300 // Use read3/write3 timing 67 | #define SMI_ADRS_MSK 0x0000003F // Address bits 0-5 to use 68 | #define SMI_ADRS_LS 0 // Address LS bit 69 | 70 | #define SMI_DATA_REG 3 //** transfer data register 71 | 72 | #define SMI_READ0_REG 4 //** Read settings 0 reg 73 | #define SMI_WRITE0_REG 5 //** Write settings 0 reg 74 | #define SMI_READ1_REG 6 //** Read settings 1 reg 75 | #define SMI_WRITE1_REG 7 //** Write settings 1 reg 76 | #define SMI_READ2_REG 8 //** Read settings 2 reg 77 | #define SMI_WRITE2_REG 9 //** Write settings 2 reg 78 | #define SMI_READ3_REG 10 //** Read settings 3 reg 79 | #define SMI_WRITE3_REG 11 //** Write settings 3 reg 80 | 81 | // SMI read and write register fields applicable 82 | // to the 8 register listed above 83 | // Beware two fields are different between read and write 84 | #define SMI_RW_WIDTH_MSK 0xC0000000 // Data width mask 85 | #define SMI_RW_WID8 0x00000000 // Data width 8 bits 86 | #define SMI_RW_WID16 0x40000000 // Data width 16 bits 87 | #define SMI_RW_WID18 0x80000000 // Data width 18 bits 88 | #define SMI_RW_WID9 0xC0000000 // Data width 9 bits 89 | #define SMI_RW_SETUP_MSK 0x3F000000 // Setup cycles (6 bits) 90 | #define SMI_RW_SETUP_LS 24 // Setup cycles LS bit (shift) 91 | #define SMI_RW_MODE68 0x00800000 // Run cycle motorola mode 92 | #define SMI_RW_MODE80 0x00000000 // Run cycle intel mode 93 | #define SMI_READ_FSETUP 0x00400000 //++ READ! Setup only for first cycle 94 | #define SMI_WRITE_SWAP 0x00400000 //++ Write! swap pixel data 95 | #define SMI_RW_HOLD_MSK 0x003F0000 // Hold cycles (6 bits) 96 | #define SMI_RW_HOLD_LS 16 // Hold cycles LS bit (shift) 97 | #define SMI_RW_PACEALL 0x00008000 // Apply pacing always 98 | // Clear: no pacing if switching to a different read/write settings 99 | #define SMI_RW_PACE_MSK 0x00007F00 // Pace cycles (7 bits) 100 | #define SMI_RW_PACE_LS 8 // Pace cycles LS bit (shift) 101 | #define SMI_RW_DREQ 0x00000080 // Use DMA req on read/write 102 | // Use with SMI_DMAC_DMAP 103 | #define SMI_RW_STROBE_MSK 0x0000007F // Strobe cycles (7 bits) 104 | #define SMI_RW_STROBE_LS 0 // Strobe cycles LS bit (shift) 105 | 106 | // DMA control register 107 | #define SMI_DMAC_REG 12 //** DMA control register 108 | #define SMI_DMAC_ENABLE 0x10000000 // DMA enable 109 | #define SMI_DMAC_DMAP 0x01000000 // D16/17 are resp DMA_REQ/DMA_ACK 110 | #define SMI_DMAC_RXPANIC_MSK 0x00FC0000 // Read Panic threshold (6 bits) 111 | #define SMI_DMAC_RXPANIC_LS 18 // Read Panic threshold LS bit (shift) 112 | #define SMI_DMAC_TXPANIC_MSK 0x0003F000 // Write Panic threshold (6 bits) 113 | #define SMI_DMAC_TXPANIC_LS 12 // Write Panic threshold LS bit (shift) 114 | #define SMI_DMAC_RXTHRES_MSK 0x00000FC0 // Read DMA threshold (6 bits) 115 | #define SMI_DMAC_RXTHRES_LS 6 // Read DMA threshold LS bit (shift) 116 | #define SMI_DMAC_TXTHRES_MSK 0x0000003F // Write DMA threshold (6 bits) 117 | #define SMI_DMAC_TXTHRES_LS 0 // Read DMA threshold LS bit (shift) 118 | // 119 | // Direct access registers 120 | // 121 | #define SMI_DIRCS_REG 13 //** Direct control register 122 | // The following fields are identical in the SMI_DC register 123 | #define SMI_DIRCS_WRITE 0x00000008 // 1= Write to ext. devices 124 | #define SMI_DIRCS_DONE 0x00000004 // Set if transfer has finished 125 | // Write with bit set to clear 126 | #define SMI_DIRCS_START 0x00000002 // Write to 1 to start transfer(s) 127 | // Found to make no difference!! 128 | #define SMI_DIRCS_ENABLE 0x00000001 // Set to enable SMI 129 | // 0= Read from ext. devices 130 | 131 | #define SMI_DIRADRS_REG 14 //** transfer address register 132 | #define SMI_DIRADRS_DEV_MSK 0x00000300 // Which device timing to use 133 | #define SMI_DIRADRS_DEV_LS 8 134 | #define SMI_DIRADRS_DEV0 0x00000000 // Use read0/write0 timing 135 | #define SMI_DIRADRS_DEV1 0x00000100 // Use read1/write1 timing 136 | #define SMI_DIRADRS_DEV2 0x00000200 // Use read2/write2 timing 137 | #define SMI_DIRADRS_DEV3 0x00000300 // Use read3/write3 timing 138 | #define SMI_DIRADRS_MSK 0x0000003F // Address bits 0-5 to use 139 | #define SMI_DIRADRS_LS 0 // Address LS bit 140 | 141 | #define SMI_DIRDATA_REG 15 //** Direct access data register 142 | 143 | // FIDO debug register 144 | // There is terse and ambigous documentation 145 | // e.g. How/when is high level reset 146 | #define SMI_FIFODBG_REG 16 //** FIFO debug register 147 | #define SMI_FIFODBG_HIGH_MSK 0x00003F00 // High level during last transfers 148 | #define SMI_FIFODBG_HIGH_LS 8 // High level LS bit 149 | #define SMI_FIFODBG_LEVL_MSK 0x0000003F // Current level 150 | #define SMI_FIFODBG_LEVL_LS 0 // Current level LS bit (shift) 151 | 152 | // 153 | // DMA 154 | // 155 | #define DMA_CS_REG(ch) (0x40*(ch)+0) //** Control and Status register 156 | #define DMA_CS_RESET 0x80000000 // Set to reset DMA 157 | #define DMA_CS_ABORT 0x40000000 // Set to abort current control block 158 | #define DMA_CS_DISDEBUG 0x20000000 // If set DMA won't be paused when debug signal is sent 159 | #define DMA_CS_WAIT_WRITES 0x10000000 // If set DMA will wait for outstanding writes 160 | #define DMA_CS_PANIC_PRI_MSK 0x00F00000 // AXI Panic Priority level (4 bits). 0 is lowest 161 | #define DMA_CS_PANIC_PRI_LS 20 // AXI Panic Priority level LS bit (shift) 162 | #define DMA_CS_PRIORITY_MSK 0x000F0000 // AXI normal Priority level (4 bits). 0 is lowest 163 | #define DMA_CS_PRIORITY_LS 16 // AXI normal Priority level LS bit (shift) 164 | #define DMA_CS_ERROR 0x00000100 // Set when error is encountered. Read only 165 | #define DMA_CS_WAITING_WRITES 0x00000040 // Set when waiting for outstanding writes. Read only 166 | #define DMA_CS_DREQ_STOPS_DMA 0x00000020 // Set when waiting for DREQ. Read only 167 | #define DMA_CS_PAUSED 0x00000010 // Set when DMA is paused. Read only 168 | #define DMA_CS_DREQ 0x00000008 // State of the selected DREQ signal. Read only 169 | #define DMA_CS_INT 0x00000004 // Set when current CB ends and its INTEN=1 170 | #define DMA_CS_END 0x00000002 // Set when transfer defined by current CB is complete 171 | #define DMA_CS_ACTIVE 0x00000001 // Set to activate DMA (load CB before hand) 172 | 173 | #define DMA_CONBLK_AD_REG(ch) (0x40*(ch)+1) //** Control Block Address register 174 | 175 | #define DMA_TI_REG(ch) (0x40*(ch)+2) //** Transfer Infomation register 176 | #define DMA_TI_NO_WIDE_BURSTS 0x04000000 // Prevents the DMA from issuing wide writes 177 | #define DMA_TI_WAITS_MSK 0x03E00000 // Add Wait Cycles (5 bits) 178 | #define DMA_TI_WAITS_LS 21 // Add Wait Cycles LS bit (shift) 179 | #define DMA_TI_PERMAP_MSK 0x001F0000 // Peripheral Mapping (5 bits) 180 | #define DMA_TI_PERMAP_LS 16 // Peripheral Mapping LS bit (shift) 181 | #define DMA_TI_BURST_LEN_MSK 0x0000F000 // Peripheral Mapping (4 bits) 182 | #define DMA_TI_BURST_LEN_LS 12 // Peripheral Mapping LS bit (shift) 183 | #define DMA_TI_SRC_IGNORE 0x00000800 // Prevents the DMA from issuing wide writes 184 | #define DMA_TI_SRC_DREQ 0x00000400 // DREQ selected by PERMAP will gate the reads 185 | #define DMA_TI_SRC_WIDTH 0x00000200 // 1 = Use 128-bit source read width. 0 = 32-bit 186 | #define DMA_TI_SRC_INC 0x00000100 // Increment source address after each read 187 | #define DMA_TI_DEST_IGNORE 0x00000080 // Do not perform destination writes 188 | #define DMA_TI_DEST_DREQ 0x00000040 // DREQ selected by PERMAP will gate the writes 189 | #define DMA_TI_DEST_WIDTH 0x00000020 // 1 = Use 128-bit destination write width. 0 = 32-bit 190 | #define DMA_TI_DEST_INC 0x00000010 // Increment destination address after each read 191 | #define DMA_TI_WAIT_RESP 0x00000008 // Wait for a write response 192 | #define DMA_TI_TDMODE 0x00000002 // Set 2D mode 193 | #define DMA_TI_INTEN 0x00000001 // Generate an interrupt when transfer completes 194 | 195 | #define DMA_SOURCE_AD_REG(ch) (0x40*(ch)+3) //** Source Address register 196 | #define DMA_DEST_AD_REG(ch) (0x40*(ch)+4) //** Destination Address register 197 | #define DMA_TXFR_LN_REG(ch) (0x40*(ch)+5) //** Transfer Length register 198 | #define DMA_STRIDE_REG(ch) (0x40*(ch)+6) //** 2D Stride register 199 | #define DMA_NEXTCONBK_REG(ch) (0x40*(ch)+7) //** Next Control Block Address register 200 | 201 | #define DMA_DEBUG_REG(ch) (0x40*(ch)+8) //** Debug register 202 | #define DMA_DEBUG_READ_ERROR 0x00000004 // Set if the read operation returned an error 203 | #define DMA_DEBUG_FIFO_ERROR 0x00000002 // Set if the read Fifo records an error condition 204 | #define DMA_DEBUG_SET_ERROR 0x00000001 // Set if the AXI read last signal was not set when expected 205 | 206 | #define DMA_ENABLE_REG 0x3FC //** Global DMA enable register 207 | 208 | // 209 | // GPIO 210 | // 211 | 212 | // GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y) 213 | #define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) 214 | #define OUT_GPIO(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3)) 215 | #define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) 216 | 217 | #define GPIO_SET0 *(gpio+7) // Set GPIO high bits 0-31 218 | #define GPIO_SET1 *(gpio+8) // Set GPIO high bits 32-53 219 | 220 | #define GPIO_CLR0 *(gpio+10) // Set GPIO low bits 0-31 221 | #define GPIO_CLR1 *(gpio+11) // Set GPIO low bits 32-53 222 | 223 | #define GPIO_LEV0 *(gpio+13) // Get GPIO level low bits 0-31 224 | 225 | #define GPIO_EDS0 *(gpio+16) // Get GPIO event detect status low bits 0-31 226 | 227 | #define GPIO_REN0 *(gpio+19) // Get GPIO rising edge detect enable low bits 0-31 228 | 229 | #define GPIO_PULL *(gpio+37) // Pull up/pull down 230 | #define GPIO_PULLCLK0 *(gpio+38) // Pull up/pull down clock 231 | 232 | // 233 | // Mailbox 234 | // 235 | #define MAILBOX_BUFFER_SIZE 0 236 | #define MAILBOX_RE_CODE 1 237 | #define MAILBOX_TAG_ID 2 238 | #define MAILBOX_TAG_BUF_SIZE 3 239 | #define MAILBOX_TAG_RE_CODE 4 240 | #define MAILBOX_TAG_BUFFER 5 241 | 242 | // Message IDs for different mailbox GPU memory allocation messages 243 | #define TAG_ALLOCATE_MEMORY 0x3000C // This message is 3 u32s: size, alignment and flags 244 | #define TAG_LOCK_MEMORY 0x3000D // 1 u32: handle 245 | #define TAG_UNLOCK_MEMORY 0x3000E // 1 u32: handle 246 | #define TAG_RELEASE_MEMORY 0x3000F // This message is 1 u32: handle 247 | 248 | // Memory allocation flags 249 | #define MEM_FLAG_DIRECT (1 << 2) // 0xC alias. Uncached 250 | #define MEM_FLAG_COHERENT (1 << 3) // 0x8 alias. Non-allocating in L2 but coherent 251 | #define MEM_FLAG_L1_NONALLOCATING (MEM_FLAG_DIRECT | MEM_FLAG_COHERENT) // Allocating in L2 252 | #define MEM_FLAG_ZERO (1 << 4) // initialise buffer to all zeros 253 | #define MEM_FLAG_NO_INIT (1 << 5) // don't initialise (default is initialise to all ones 254 | #define MEM_FLAG_HINT_PERMALOCK (1 << 6) // Likely to be locked for long periods of time 255 | 256 | // Response codes 257 | #define BUF_REQ_SUCCESSFUL 0x80000000 258 | #define TAG_RE_BIT_MASK 0x80000000 259 | 260 | #define BUS_TO_PHYS(x) ((x) & ~0xC0000000) 261 | #define PHYS_TO_BUS(x) ((x) | 0xC0000000) 262 | 263 | -------------------------------------------------------------------------------- /pi/sound.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Based on SID_Be.i - 6581 emulation, Be specific stuff 3 | * Frodo (C) 1994-1997,2002 Christian Bauer 4 | */ 5 | 6 | #include 7 | 8 | static uint32_t dev; 9 | 10 | /* 11 | * Callback function 12 | */ 13 | 14 | static void audio_callback(void *userdata, uint8_t *stream, int len) 15 | { 16 | (void)userdata; // ignore 17 | calc_buffer((int16_t *)stream, len); 18 | } 19 | 20 | /* 21 | * Initialization 22 | */ 23 | 24 | static void sound_init(void) 25 | { 26 | SDL_AudioSpec want, have; 27 | ready = false; 28 | 29 | SDL_memset(&want, 0, sizeof(want)); 30 | want.freq = SAMPLE_FREQ; 31 | want.format = AUDIO_S16LSB; 32 | want.channels = 1; 33 | want.samples = SAMPLE_FREQ / CALC_FREQ; 34 | want.callback = audio_callback; 35 | want.userdata = NULL; 36 | 37 | dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0); 38 | if (dev == 0) 39 | { 40 | fprintf(stderr, "Failed to open audio: %s", SDL_GetError()); 41 | return; 42 | } 43 | 44 | SDL_PauseAudioDevice(dev, 0); // start audio playing 45 | ready = true; 46 | } 47 | 48 | 49 | /* 50 | * Close audio device 51 | */ 52 | 53 | static void sound_close() 54 | { 55 | SDL_CloseAudioDevice(dev); 56 | } 57 | 58 | 59 | /* 60 | * Sample volume (for sampled voice) 61 | */ 62 | 63 | void static emulate_line_sound(void) 64 | { 65 | SDL_LockAudioDevice(dev); 66 | 67 | sample_buf[sample_in_ptr] = volume; 68 | sample_in_ptr = (sample_in_ptr + 1) % SAMPLE_BUF_SIZE; 69 | 70 | SDL_UnlockAudioDevice(dev); 71 | } 72 | 73 | -------------------------------------------------------------------------------- /pics/board.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KimJorgensen/C64_Pi_Interface/b0c960458a04053751bff722c0359092d4143f57/pics/board.jpg -------------------------------------------------------------------------------- /pics/data_transfer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KimJorgensen/C64_Pi_Interface/b0c960458a04053751bff722c0359092d4143f57/pics/data_transfer.jpg -------------------------------------------------------------------------------- /pics/in_action.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KimJorgensen/C64_Pi_Interface/b0c960458a04053751bff722c0359092d4143f57/pics/in_action.jpg -------------------------------------------------------------------------------- /vhdl/Makefile: -------------------------------------------------------------------------------- 1 | ifeq "$(release)" "yes" 2 | version := 1.0 3 | else 4 | version := $(shell date +%y%m%d-%H%M) 5 | endif 6 | 7 | here := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) 8 | synth_dir := $(here)/synth 9 | src_dir := $(here)/src 10 | vhdlprj := cpi 11 | 12 | # this file list is for dependencies only, keep in sync with $(vhdlprj).prj 13 | src := $(src_dir)/$(vhdlprj).vhdl 14 | src += $(src_dir)/exp_bus_ctrl.vhdl 15 | 16 | uc := $(src_dir)/$(vhdlprj).ucf 17 | 18 | xst_cfg := $(here)/$(vhdlprj).xst 19 | project := $(here)/$(vhdlprj).prj 20 | 21 | #netlist := $(addprefix $(synth_dir)/, $(notdir $(src:.vhdl=.ngc))) 22 | netlist := $(synth_dir)/$(vhdlprj).ngc 23 | 24 | gen_db := $(synth_dir)/$(vhdlprj).ngd 25 | fit := $(synth_dir)/$(vhdlprj).vm6 26 | jedec := $(here)/$(vhdlprj).jed 27 | svf := $(here)/$(vhdlprj).svf 28 | 29 | dev_type := xc95144xl 30 | dev_pkg := tq100 31 | dev_speed := 10 32 | device := $(dev_type)-$(dev_pkg)-$(dev_speed) 33 | device_fit := $(dev_type)-$(dev_speed)-$(dev_pkg) 34 | 35 | fit_flags := -p $(device_fit) -ofmt abel -log fitter.log -optimize density 36 | fit_flags += -power low -slew slow 37 | #fit_flags += -exhaust 38 | fit_flags += -inputs 54 -pterms 25 39 | fit_filter_output := "^CS: block property:\|^$$" 40 | 41 | # directories to be created 42 | dirs := $(synth_dir) $(here)/log 43 | 44 | .PHONY: all 45 | ifeq "$(release)" "yes" 46 | all: $(vhdlprj)-cpld-$(version).zip 47 | else 48 | all: $(svf) 49 | endif 50 | 51 | ################################################################################ 52 | $(vhdlprj)-cpld-$(version).zip: $(svf) README.txt 53 | rm -rf $(vhdlprj)-cpld-$(version) 54 | rm -f $@ 55 | mkdir $(vhdlprj)-cpld-$(version) 56 | cp $(svf) $(vhdlprj)-cpld-$(version)/$(vhdlprj)-cpld-$(version).svf 57 | cp README.txt $(vhdlprj)-cpld-$(version) 58 | zip -v -r $@ $(vhdlprj)-cpld-$(version) 59 | 60 | ################################################################################ 61 | .PHONY: netlist 62 | netlist: $(netlist) 63 | $(netlist): $(xst_cfg) $(src) $(project) | $(dirs) 64 | mkdir -p $(synth_dir)/xst/tmp/ 65 | xst -intstyle silent -ifn $< 66 | mv $(vhdlprj).srp log/netlist.$(vhdlprj).srp 67 | 68 | ################################################################################ 69 | .PHONY: gen_db 70 | gen_db: $(gen_db) 71 | $(gen_db): $(netlist) $(uc) | $(dirs) 72 | mkdir -p synth/ngdbuild/tmp/ 73 | cd $(synth_dir) && ngdbuild -p $(device) -uc $(uc) -quiet \ 74 | -intstyle silent -dd $(synth_dir)/ngdbuild/tmp/ $< 75 | mv $(synth_dir)/$(vhdlprj).bld log/ngd.$(vhdlprj).bld 76 | 77 | ################################################################################ 78 | .PHONY: fit 79 | fit: $(fit) 80 | $(fit): $(gen_db) | $(dirs) 81 | cd $(synth_dir) && cpldfit $(fit_flags) $< | grep -v $(fit_filter_output) 82 | mv $(synth_dir)/$(vhdlprj).rpt log/fitter.$(vhdlprj).rpt 83 | 84 | ################################################################################ 85 | .PHONY: jedec 86 | jedec: $(jedec) 87 | $(jedec): $(fit) 88 | hprep6 -i $< 89 | 90 | ################################################################################ 91 | .PHONY: svf 92 | svf: $(svf) 93 | $(svf): $(jedec) 94 | cat impact.batch | impact -batch 95 | mv _impactbatch.log log/ 96 | 97 | ################################################################################ 98 | .PHONY: usbprog 99 | usbprog: $(svf) 100 | svfplayer $< 101 | 102 | ################################################################################ 103 | .PHONY: prog 104 | prog: $(svf) 105 | easp -p 0x8738 $< 106 | 107 | ################################################################################ 108 | $(dirs): 109 | mkdir -p $@ 110 | 111 | ################################################################################ 112 | test: 113 | mkdir -p $(here)/bench 114 | export XILINX=/opt/Xilinx/14.7/ISE_DS/ && \ 115 | bash /opt/Xilinx/14.7/ISE_DS/settings64.sh && \ 116 | cd $(here)/bench && \ 117 | fuse -intstyle ise -incremental -o test_bench -prj ../test_bench.prj test_bench && \ 118 | ./test_bench -tclbatch ../test_bench.cmd -gui 119 | 120 | ################################################################################ 121 | .PHONY: clean 122 | clean: 123 | rm -rf synth 124 | rm -rf log 125 | rm -rf _xmsgs 126 | rm -rf bench 127 | rm -f $(vhdlprj).jed 128 | rm -f $(vhdlprj).svf 129 | rm -f tmperr.err 130 | -------------------------------------------------------------------------------- /vhdl/README.txt: -------------------------------------------------------------------------------- 1 | C64 - Pi Interface CPLD Firmware version 1.0, July 2019 are 2 | Copyright (c) 2019 Kim Jorgensen, are derived from EasyFlash 3 CPLD Firmware 1.1.1, 3 | and are distributed according to the same disclaimer and license as 4 | EasyFlash 3 CPLD Firmware 1.1.1 5 | 6 | EasyFlash 3 CPLD Firmware versions 0.9.0, December 2011, through 1.1.1, August 2012, are 7 | Copyright (c) 2011-2012 Thomas 'skoe' Giesel 8 | 9 | License 10 | ======= 11 | 12 | -- This software is provided 'as-is', without any express or implied 13 | -- warranty. In no event will the authors be held liable for any damages 14 | -- arising from the use of this software. 15 | -- 16 | -- Permission is granted to anyone to use this software for any purpose, 17 | -- including commercial applications, and to alter it and redistribute it 18 | -- freely, subject to the following restrictions: 19 | -- 20 | -- 1. The origin of this software must not be misrepresented; you must not 21 | -- claim that you wrote the original software. If you use this software 22 | -- in a product, an acknowledgment in the product documentation would be 23 | -- appreciated but is not required. 24 | -- 2. Altered source versions must be plainly marked as such, and must not be 25 | -- misrepresented as being the original software. 26 | -- 3. This notice may not be removed or altered from any source distribution. 27 | 28 | 29 | Changes 30 | ======= 31 | 32 | Version 1.0 - 08.07.2019 33 | 34 | - First release 35 | -------------------------------------------------------------------------------- /vhdl/cpi.prj: -------------------------------------------------------------------------------- 1 | vhdl work "src/exp_bus_ctrl.vhdl" 2 | vhdl work "src/cpi.vhdl" 3 | -------------------------------------------------------------------------------- /vhdl/cpi.xst: -------------------------------------------------------------------------------- 1 | set -tmpdir synth/xst/tmp 2 | set -xsthdpdir synth/xst 3 | run 4 | -ifn cpi.prj 5 | -ifmt vhdl 6 | -ofn synth/cpi.ngc 7 | -ofmt NGC 8 | -p xc9500xl 9 | -top cpi 10 | -opt_mode area 11 | -opt_level 2 12 | -iuc NO 13 | -keep_hierarchy no 14 | -netlist_hierarchy As_Optimized 15 | -rtlview Yes 16 | -hierarchy_separator / 17 | -bus_delimiter <> 18 | -fsm_extract YES -fsm_encoding Auto 19 | -safe_implementation No 20 | -mux_extract Yes 21 | -resource_sharing YES 22 | -iobuf YES 23 | -pld_mp no 24 | -pld_xp no 25 | -pld_ce YES 26 | -wysiwyg NO 27 | -equivalent_register_removal YES 28 | -------------------------------------------------------------------------------- /vhdl/impact.batch: -------------------------------------------------------------------------------- 1 | setMode -bs 2 | addDevice -p 1 -file cpi.jed 3 | setCable -port svf -file cpi.svf 4 | program -e -v -p 1 5 | closeCable 6 | quit 7 | -------------------------------------------------------------------------------- /vhdl/src/cpi.ucf: -------------------------------------------------------------------------------- 1 | NET "n_reset" LOC = "P99" | SLEW = SLOW | BUFG = SR; 2 | NET "n_wr" LOC = "P1" | SLEW = SLOW; 3 | NET "ba" LOC = "P2" | SLEW = SLOW; 4 | NET "n_dma" LOC = "P3" | SLEW = SLOW; 5 | NET "n_io1" LOC = "P4" | SLEW = SLOW; 6 | NET "addr<11>" LOC = "P6" | SLEW = SLOW; 7 | NET "n_roml" LOC = "P7" | SLEW = SLOW; 8 | NET "addr<10>" LOC = "P8" | SLEW = SLOW; 9 | NET "addr<9>" LOC = "P9" | SLEW = SLOW; 10 | NET "addr<8>" LOC = "P10" | SLEW = SLOW; 11 | 12 | NET "addr<7>" LOC = "P11" | SLEW = SLOW; 13 | NET "addr<6>" LOC = "P12" | SLEW = SLOW; 14 | NET "addr<5>" LOC = "P13" | SLEW = SLOW; 15 | NET "addr<4>" LOC = "P14" | SLEW = SLOW; 16 | NET "data<7>" LOC = "P15" | SLEW = SLOW; 17 | NET "data<6>" LOC = "P16" | SLEW = SLOW; 18 | NET "data<5>" LOC = "P17" | SLEW = SLOW; 19 | NET "data<4>" LOC = "P18" | SLEW = SLOW; 20 | NET "addr<3>" LOC = "P19" | SLEW = SLOW; 21 | NET "data<3>" LOC = "P20" | SLEW = SLOW; 22 | NET "d_clk" LOC = "P22" | SLEW = SLOW | BUFG = CLK; 23 | 24 | NET "clk" LOC = "P23" | SLEW = SLOW | BUFG = CLK; 25 | NET "addr<2>" LOC = "P24" | SLEW = SLOW; 26 | NET "data<2>" LOC = "P25" | SLEW = SLOW; 27 | NET "phi2" LOC = "P27" | SLEW = SLOW | BUFG = CLK; 28 | NET "data<1>" LOC = "P28" | SLEW = SLOW; 29 | NET "addr<1>" LOC = "P29" | SLEW = SLOW; 30 | NET "data<0>" LOC = "P30" | SLEW = SLOW; 31 | NET "addr<0>" LOC = "P32" | SLEW = SLOW; 32 | NET "tp1" LOC = "P33" | SLEW = SLOW; 33 | NET "tp2" LOC = "P34" | SLEW = SLOW; 34 | 35 | NET "tp3" LOC = "P35" | SLEW = SLOW; 36 | NET "tp4" LOC = "P36" | SLEW = SLOW; 37 | NET "tp5" LOC = "P37" | SLEW = SLOW; 38 | NET "n_wp" LOC = "P39" | SLEW = SLOW; 39 | NET "miso" LOC = "P40" | SLEW = SLOW; 40 | NET "mosi" LOC = "P41" | SLEW = SLOW; 41 | NET "sck" LOC = "P42" | SLEW = SLOW; 42 | NET "n_hold" LOC = "P43" | SLEW = SLOW; 43 | NET "n_ram_cs" LOC = "P46" | SLEW = SLOW; 44 | NET "n_flash_cs" LOC = "P49" | SLEW = SLOW; 45 | 46 | NET "n_sw2" LOC = "P50" | SLEW = SLOW; 47 | NET "n_led" LOC = "P52" | SLEW = SLOW; 48 | NET "sa<3>" LOC = "P53" | SLEW = SLOW; 49 | NET "sa<2>" LOC = "P54" | SLEW = SLOW; 50 | NET "sa<1>" LOC = "P55" | SLEW = SLOW; 51 | NET "sd<6>" LOC = "P56" | SLEW = SLOW; 52 | NET "sd<7>" LOC = "P58" | SLEW = SLOW; 53 | NET "sd<9>" LOC = "P59" | SLEW = SLOW; 54 | NET "sd<10>" LOC = "P60" | SLEW = SLOW; 55 | ## NET "gpio<2>" LOC = "P53" | SLEW = SLOW; 56 | ## NET "gpio<3>" LOC = "P54" | SLEW = SLOW; 57 | ## NET "gpio<4>" LOC = "P55" | SLEW = SLOW; 58 | ## NET "gpio<14>" LOC = "P56" | SLEW = SLOW; 59 | ## NET "gpio<15>" LOC = "P58" | SLEW = SLOW; 60 | ## NET "gpio<17>" LOC = "P59" | SLEW = SLOW; 61 | ## NET "gpio<18>" LOC = "P60" | SLEW = SLOW; 62 | NET "gpio<27>" LOC = "P61" | SLEW = SLOW; 63 | 64 | NET "sd<14>" LOC = "P63" | SLEW = SLOW; 65 | NET "sd<15>" LOC = "P64" | SLEW = SLOW; 66 | NET "sdrr" LOC = "P65" | SLEW = SLOW; 67 | NET "sd<2>" LOC = "P66" | SLEW = SLOW; 68 | NET "sd<1>" LOC = "P67" | SLEW = SLOW; 69 | NET "sdwr" LOC = "P68" | SLEW = SLOW; 70 | NET "sd<3>" LOC = "P70" | SLEW = SLOW; 71 | NET "sd<0>" LOC = "P71" | SLEW = SLOW; 72 | NET "n_swe" LOC = "P72" | SLEW = SLOW; 73 | NET "sa<5>" LOC = "P73" | SLEW = SLOW; 74 | ## NET "gpio<22>" LOC = "P63" | SLEW = SLOW; 75 | ## NET "gpio<23>" LOC = "P64" | SLEW = SLOW; 76 | ## NET "gpio<24>" LOC = "P65" | SLEW = SLOW; 77 | ## NET "gpio<10>" LOC = "P66" | SLEW = SLOW; 78 | ## NET "gpio<9>" LOC = "P67" | SLEW = SLOW; 79 | ## NET "gpio<25>" LOC = "P68" | SLEW = SLOW; 80 | ## NET "gpio<11>" LOC = "P70" | SLEW = SLOW; 81 | ## NET "gpio<8>" LOC = "P71" | SLEW = SLOW; 82 | ## NET "gpio<7>" LOC = "P72" | SLEW = SLOW; 83 | ## NET "gpio<0>" LOC = "P73" | SLEW = SLOW; 84 | 85 | NET "sa<4>" LOC = "P74" | SLEW = SLOW; 86 | NET "sa<0>" LOC = "P76" | SLEW = SLOW; 87 | NET "n_soe" LOC = "P77" | SLEW = SLOW; 88 | NET "sd<4>" LOC = "P78" | SLEW = SLOW; 89 | NET "sd<5>" LOC = "P79" | SLEW = SLOW; 90 | NET "sd<11>" LOC = "P80" | SLEW = SLOW; 91 | NET "sd<8>" LOC = "P81" | SLEW = SLOW; 92 | NET "gpio<26>" LOC = "P82" | SLEW = SLOW; 93 | NET "sd<12>" LOC = "P85" | SLEW = SLOW; 94 | NET "sd<13>" LOC = "P86" | SLEW = SLOW; 95 | ## NET "gpio<1>" LOC = "P74" | SLEW = SLOW; 96 | ## NET "gpio<5>" LOC = "P76" | SLEW = SLOW; 97 | ## NET "gpio<6>" LOC = "P77" | SLEW = SLOW; 98 | ## NET "gpio<12>" LOC = "P78" | SLEW = SLOW; 99 | ## NET "gpio<13>" LOC = "P79" | SLEW = SLOW; 100 | ## NET "gpio<19>" LOC = "P80" | SLEW = SLOW; 101 | ## NET "gpio<16>" LOC = "P81" | SLEW = SLOW; 102 | ## NET "gpio<20>" LOC = "P85" | SLEW = SLOW; 103 | ## NET "gpio<21>" LOC = "P86" | SLEW = SLOW; 104 | 105 | NET "n_romh" LOC = "P87" | SLEW = SLOW; 106 | NET "n_irq" LOC = "P89" | SLEW = SLOW; 107 | NET "n_nmi" LOC = "P90" | SLEW = SLOW; 108 | NET "addr<15>" LOC = "P91" | SLEW = SLOW; 109 | NET "addr<14>" LOC = "P92" | SLEW = SLOW; 110 | NET "n_game" LOC = "P93" | SLEW = SLOW; 111 | NET "addr<13>" LOC = "P94" | SLEW = SLOW; 112 | NET "n_exrom" LOC = "P95" | SLEW = SLOW; 113 | NET "addr<12>" LOC = "P96" | SLEW = SLOW; 114 | NET "n_io2" LOC = "P97" | SLEW = SLOW; 115 | 116 | -------------------------------------------------------------------------------- /vhdl/src/cpi.vhdl: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------------------- 2 | -- 3 | -- C64 - Pi Interface CPLD Firmware version 1.0, July 2019 are 4 | -- Copyright (c) 2019 Kim Jorgensen, are derived from EasyFlash 3 CPLD Firmware 1.1.1, 5 | -- and are distributed according to the same disclaimer and license as 6 | -- EasyFlash 3 CPLD Firmware 1.1.1 7 | -- 8 | -- EasyFlash 3 CPLD Firmware versions 0.9.0, December 2011, through 1.1.1, August 2012, are 9 | -- Copyright (c) 2011-2012 Thomas 'skoe' Giesel 10 | -- 11 | -- This software is provided 'as-is', without any express or implied 12 | -- warranty. In no event will the authors be held liable for any damages 13 | -- arising from the use of this software. 14 | -- 15 | -- Permission is granted to anyone to use this software for any purpose, 16 | -- including commercial applications, and to alter it and redistribute it 17 | -- freely, subject to the following restrictions: 18 | -- 19 | -- 1. The origin of this software must not be misrepresented; you must not 20 | -- claim that you wrote the original software. If you use this software 21 | -- in a product, an acknowledgment in the product documentation would be 22 | -- appreciated but is not required. 23 | -- 2. Altered source versions must be plainly marked as such, and must not be 24 | -- misrepresented as being the original software. 25 | -- 3. This notice may not be removed or altered from any source distribution. 26 | -- 27 | ---------------------------------------------------------------------------------- 28 | 29 | 30 | library ieee; 31 | use ieee.std_logic_1164.all; 32 | use ieee.numeric_std.all; 33 | 34 | entity cpi is 35 | port ( 36 | addr: in std_logic_vector (15 downto 0); 37 | data: inout std_logic_vector (7 downto 0); 38 | n_dma: out std_logic; 39 | ba: in std_logic; 40 | n_roml: in std_logic; 41 | n_romh: in std_logic; 42 | n_io1: in std_logic; 43 | n_io2: in std_logic; 44 | n_wr: in std_logic; 45 | n_irq: out std_logic; 46 | n_nmi: out std_logic; 47 | n_reset: in std_logic; 48 | d_clk: in std_logic; 49 | clk: in std_logic; 50 | phi2: in std_logic; 51 | n_exrom: out std_logic; 52 | n_game: out std_logic; 53 | n_sw2: in std_logic; 54 | n_led: out std_logic; 55 | sa: in std_logic_vector (5 downto 0); 56 | sd: out std_logic_vector (15 downto 0); 57 | sdrr: out std_logic; -- SD16 58 | sdwr: out std_logic; -- SD17 59 | n_soe: in std_logic; 60 | n_swe: in std_logic; 61 | gpio: in std_logic_vector (27 downto 26); 62 | n_flash_cs: out std_logic; 63 | n_ram_cs: out std_logic; 64 | sck: out std_logic; 65 | miso: out std_logic; 66 | n_wp: out std_logic; 67 | mosi: out std_logic; 68 | n_hold: out std_logic; 69 | tp1: out std_logic; 70 | tp2: out std_logic; 71 | tp3: out std_logic; 72 | tp4: out std_logic; 73 | tp5: out std_logic 74 | ); 75 | end cpi; 76 | 77 | architecture cpi_arc of cpi is 78 | signal rd: std_logic; 79 | signal wr: std_logic; 80 | signal rp: std_logic; 81 | signal wp: std_logic; 82 | signal wp_end: std_logic; 83 | signal cycle_time: std_logic_vector(10 downto 0); 84 | signal cycle_start: std_logic; 85 | 86 | signal data_out: std_logic_vector(7 downto 0); 87 | signal data_out_valid: std_logic; 88 | 89 | signal n_exrom_out: std_logic; 90 | signal n_game_out: std_logic; 91 | 92 | component exp_bus_ctrl is 93 | port ( 94 | clk: in std_logic; 95 | phi2: in std_logic; 96 | n_wr: in std_logic; 97 | rd: out std_logic; 98 | wr: out std_logic; 99 | rp: out std_logic; 100 | wp: out std_logic; 101 | wp_end: out std_logic; 102 | cycle_time: out std_logic_vector(10 downto 0); 103 | cycle_start: out std_logic 104 | ); 105 | end component; 106 | 107 | signal clk_div2: std_logic; 108 | 109 | signal addr_i: std_logic_vector (15 downto 0); 110 | signal data_i: std_logic_vector (7 downto 0); 111 | signal n_wr_i: std_logic; 112 | signal ba_i: std_logic; 113 | 114 | signal addr_read: std_logic; 115 | signal data_read: std_logic; 116 | 117 | -- signal test_register: std_logic_vector(7 downto 0); 118 | 119 | begin 120 | --------------------------------------------------------------------------- 121 | -- Components 122 | --------------------------------------------------------------------------- 123 | u_exp_bus_ctrl: exp_bus_ctrl port map 124 | ( 125 | clk => clk, 126 | phi2 => phi2, 127 | n_wr => n_wr, 128 | rd => rd, 129 | wr => wr, 130 | rp => rp, 131 | wp => wp, 132 | wp_end => wp_end, 133 | cycle_time => cycle_time, 134 | cycle_start => cycle_start 135 | ); 136 | 137 | --------------------------------------------------------------------------- 138 | -- divider by two - TODO: Move to new component 139 | --------------------------------------------------------------------------- 140 | 141 | p_clk_div2: process(clk) 142 | begin 143 | if rising_edge(clk) then 144 | if n_reset = '0' then 145 | clk_div2 <= '0'; 146 | else 147 | clk_div2 <= not clk_div2; 148 | end if; 149 | end if; 150 | end process p_clk_div2; 151 | 152 | -- SPI Flash & RAM - TODO: Move to new component 153 | sck <= clk_div2; 154 | n_ram_cs <= '1'; 155 | n_flash_cs <= '1'; 156 | n_wp <= '1'; 157 | n_hold <= '1'; 158 | mosi <= '1'; 159 | miso <= 'Z'; 160 | 161 | -- unused signals and defaults 162 | n_exrom <= 'Z'; 163 | n_game <= 'Z'; 164 | n_dma <= 'Z'; 165 | n_nmi <= 'Z'; 166 | n_irq <= 'Z'; 167 | 168 | tp1 <= 'Z'; 169 | tp2 <= 'Z'; 170 | tp3 <= 'Z'; 171 | tp4 <= 'Z'; 172 | tp5 <= 'Z'; 173 | 174 | sdwr <= 'Z'; 175 | 176 | --------------------------------------------------------------------------- 177 | -- LED / button test 178 | --------------------------------------------------------------------------- 179 | -- led_test: process(clk, n_reset, n_sw2) 180 | -- begin 181 | -- if n_reset = '0' then 182 | -- n_led <= '1'; 183 | -- elsif rising_edge(clk) then 184 | -- if n_sw2 = '0' then 185 | -- n_led <= '0'; 186 | -- elsif n_io1 = '0' and wp = '1' then 187 | -- n_led <= not data(0); 188 | -- end if; 189 | -- end if; 190 | -- end process; 191 | 192 | --------------------------------------------------------------------------- 193 | -- Write to test register 1 at $de00-$deff 194 | --------------------------------------------------------------------------- 195 | -- w_test_reg1: process(clk, n_reset, n_io1, wp) 196 | -- begin 197 | -- if n_reset = '0' then 198 | -- test_register <= x"00"; 199 | -- elsif rising_edge(clk) then 200 | -- if n_io1 = '0' and wp = '1' then 201 | -- test_register <= data; 202 | -- end if; 203 | -- end if; 204 | -- end process; 205 | 206 | --------------------------------------------------------------------------- 207 | -- Read from test register 1 + 2 at $df00-$dfff 208 | --------------------------------------------------------------------------- 209 | -- r_test_reg: process(n_io1, n_io2, rd) 210 | -- begin 211 | -- data_out_valid <= '0'; 212 | -- if n_io1 = '0' and rd = '1' then 213 | -- data_out <= test_register; 214 | -- data_out_valid <= not cycle_start; 215 | -- elsif n_io2 = '0' and rd = '1' then 216 | -- data_out <= addr(7 downto 0); 217 | -- data_out_valid <= not cycle_start; 218 | -- end if; 219 | -- end process; 220 | 221 | --------------------------------------------------------------------------- 222 | -- Capture C64 bus in register 223 | --------------------------------------------------------------------------- 224 | smi_capture_bus: process(n_reset, phi2) 225 | begin 226 | if n_reset = '0' then 227 | addr_i <= (others => '0'); 228 | data_i <= (others => '0'); 229 | n_wr_i <= '0'; 230 | ba_i <= '0'; 231 | elsif falling_edge(phi2) then 232 | addr_i <= addr; -- TODO: Add overflow flag? 233 | data_i <= data; 234 | n_wr_i <= n_wr; 235 | ba_i <= ba; 236 | end if; 237 | end process; 238 | 239 | --------------------------------------------------------------------------- 240 | -- Set external DMA read request - TODO: Make 'Z' when not active? 241 | --------------------------------------------------------------------------- 242 | smi_data_ready: process(n_reset, clk, phi2, data_read, addr_read) 243 | begin 244 | if n_reset = '0' then 245 | sdrr <= '0'; 246 | elsif data_read = '1' then 247 | sdrr <= '0'; 248 | elsif rising_edge(clk) then 249 | if phi2 = '0' and (addr_read = '0' or data_read = '0') then 250 | sdrr <= '1'; 251 | elsif addr_read = '1' and data_read = '0' then 252 | sdrr <= '1'; 253 | else 254 | sdrr <= '0'; 255 | end if; 256 | end if; 257 | end process; 258 | 259 | --------------------------------------------------------------------------- 260 | -- Read C64 bus from register 261 | --------------------------------------------------------------------------- 262 | smi_oe: process(n_reset, n_soe, phi2, cycle_time, addr_read) 263 | begin 264 | if n_reset = '0' then 265 | addr_read <= '1'; 266 | data_read <= '1'; 267 | elsif phi2 = '1' and cycle_time(9) = '1' then 268 | addr_read <= '0'; 269 | data_read <= '0'; 270 | elsif falling_edge(n_soe) then 271 | if addr_read = '0' then -- TODO: Add underflow flag? 272 | addr_read <= '1'; 273 | else 274 | data_read <= '1'; 275 | end if; 276 | end if; 277 | end process; 278 | 279 | smi_read: process(n_soe, data_read) 280 | begin 281 | if n_soe = '0' and data_read = '0' then 282 | sd <= addr_i; 283 | elsif n_soe = '0' and data_read = '1' then 284 | sd <= "000000" & not ba_i & not n_wr_i & data_i; 285 | else 286 | sd <= (others => 'Z'); 287 | end if; 288 | end process; 289 | 290 | n_led <= sa(0); 291 | 292 | --------------------------------------------------------------------------- 293 | -- Combinatorially decide: 294 | -- - If we put the memory bus onto the C64 data bus 295 | -- - If we put data out onto the C64 data bus 296 | -- - If we put the C64 data bus onto the memory bus 297 | -- 298 | -- The C64 data bus is only driven if it is a read access with any 299 | -- of the four Expansion Port control lines asserted. 300 | -- 301 | -- The memory bus is always driven by the CPLD when no memory chip has 302 | -- OE active. 303 | -- 304 | -- We need a special case with phi2 = '0' for C128 which doesn't set R/W 305 | -- correctly for Phi1 cycles. 306 | -- 307 | --------------------------------------------------------------------------- 308 | data_out_enable: process(n_io1, n_io2, n_roml, n_romh, phi2, n_wr, 309 | data_out, data_out_valid, data) 310 | begin 311 | data <= (others => 'Z'); 312 | if (n_io1 and n_io2 and n_roml and n_romh) = '0' and 313 | ((n_wr = '1' and phi2 = '1') or phi2 = '0') then 314 | if data_out_valid = '1' then 315 | data <= data_out; 316 | end if; 317 | end if; 318 | end process data_out_enable; 319 | 320 | end cpi_arc; 321 | -------------------------------------------------------------------------------- /vhdl/src/exp_bus_ctrl.vhdl: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------------------- 2 | -- 3 | -- C64 - Pi Interface CPLD Firmware version 1.0, July 2019 are 4 | -- Copyright (c) 2019 Kim Jorgensen, are derived from EasyFlash 3 CPLD Firmware 1.1.1, 5 | -- and are distributed according to the same disclaimer and license as 6 | -- EasyFlash 3 CPLD Firmware 1.1.1 7 | -- 8 | -- EasyFlash 3 CPLD Firmware versions 0.9.0, December 2011, through 1.1.1, August 2012, are 9 | -- Copyright (c) 2011-2012 Thomas 'skoe' Giesel 10 | -- 11 | -- This software is provided 'as-is', without any express or implied 12 | -- warranty. In no event will the authors be held liable for any damages 13 | -- arising from the use of this software. 14 | -- 15 | -- Permission is granted to anyone to use this software for any purpose, 16 | -- including commercial applications, and to alter it and redistribute it 17 | -- freely, subject to the following restrictions: 18 | -- 19 | -- 1. The origin of this software must not be misrepresented; you must not 20 | -- claim that you wrote the original software. If you use this software 21 | -- in a product, an acknowledgment in the product documentation would be 22 | -- appreciated but is not required. 23 | -- 2. Altered source versions must be plainly marked as such, and must not be 24 | -- misrepresented as being the original software. 25 | -- 3. This notice may not be removed or altered from any source distribution. 26 | -- 27 | ---------------------------------------------------------------------------------- 28 | 29 | library ieee; 30 | use ieee.std_logic_1164.all; 31 | use ieee.numeric_std.all; 32 | 33 | 34 | entity exp_bus_ctrl is 35 | port ( 36 | clk: in std_logic; 37 | phi2: in std_logic; 38 | n_wr: in std_logic; 39 | rd: out std_logic; 40 | wr: out std_logic; 41 | rp: out std_logic; 42 | wp: out std_logic; 43 | wp_end: out std_logic; 44 | 45 | -- The time in a Phi2 half cycle as shift register. This is used 46 | -- as one-hot encoded state machine. 47 | cycle_time: out std_logic_vector(10 downto 0); 48 | 49 | -- This combinatorial signal is '1' for one clk cycle 50 | -- at the beginning of each Phi2 half cycle 51 | cycle_start: out std_logic 52 | ); 53 | end exp_bus_ctrl; 54 | 55 | 56 | architecture a of exp_bus_ctrl is 57 | signal prev_phi2: std_logic; 58 | signal phi2_s: std_logic; 59 | signal cycle_time_i: std_logic_vector(10 downto 0); 60 | begin 61 | 62 | --------------------------------------------------------------------------- 63 | -- Create a synchronized version of Phi2 (phi2_s) and a copy of phi2_s 64 | -- which is delayed by one clock cycle (prev_phi2). 65 | --------------------------------------------------------------------------- 66 | synchronize_stuff: process(clk) 67 | begin 68 | if rising_edge(clk) then 69 | prev_phi2 <= phi2_s; 70 | phi2_s <= phi2; 71 | end if; 72 | end process synchronize_stuff; 73 | 74 | --------------------------------------------------------------------------- 75 | -- Count clk cycles in both phases of phi2 with a shift register. 76 | --------------------------------------------------------------------------- 77 | clk_time_shift: process(clk, prev_phi2, phi2_s) 78 | begin 79 | if rising_edge(clk) then 80 | if prev_phi2 /= phi2_s then 81 | cycle_time_i <= (others => '0'); 82 | cycle_time_i(0) <= '1'; 83 | else 84 | cycle_time_i <= cycle_time_i(9 downto 0) & '0'; 85 | end if; 86 | end if; 87 | end process; 88 | 89 | cycle_time <= cycle_time_i; 90 | 91 | --------------------------------------------------------------------------- 92 | -- Write is only allowed at phi2 = '1', because on C128 it happens 93 | -- that n_wr = '0' when phi2 = '0', which is not a write access. 94 | -- 95 | -- We have partial support for C128 2 MHz mode. C128 read accesses with 96 | -- 2 MHz are at least to be supported for EasyFlash mode (e.g. for PoP) 97 | -- For this we allow read accesses to be evaluated asynchronously. 98 | -- 99 | --------------------------------------------------------------------------- 100 | check_rw: process(phi2_s, n_wr) 101 | begin 102 | wr <= '0'; 103 | rd <= '0'; 104 | 105 | if n_wr = '1' or phi2_s = '0' then 106 | rd <= '1'; 107 | end if; 108 | 109 | if n_wr = '0' and phi2_s = '1' then 110 | wr <= '1'; 111 | end if; 112 | end process; 113 | 114 | --------------------------------------------------------------------------- 115 | -- Create control signals depending from clk counter 116 | -- 117 | -- These signals are generated combinatorially, they are to be used on the 118 | -- next rising edge of clk. 119 | -- 120 | --------------------------------------------------------------------------- 121 | cycle_start <= phi2_s xor prev_phi2; 122 | 123 | -- Write pulse for internal registers 124 | -- or to start a write signal for external memory 125 | wp <= not n_wr and phi2_s and cycle_time_i(7); 126 | 127 | -- end of write access, we need 80 ns for external chips 128 | wp_end <= cycle_time_i(9); 129 | 130 | -- Read pulse (for synchronous read accesses, e.g. USB) 131 | rp <= (n_wr or not phi2_s) and cycle_time_i(6); 132 | end a; 133 | -------------------------------------------------------------------------------- /vhdl/src/test_bench.vhd: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- 3 | -- Test Bench for C64 - Pi Interface 4 | -- 5 | -------------------------------------------------------------------------------- 6 | LIBRARY ieee; 7 | USE ieee.std_logic_1164.ALL; 8 | 9 | ENTITY test_bench IS 10 | END test_bench; 11 | 12 | ARCHITECTURE behavior OF test_bench IS 13 | COMPONENT cpi 14 | PORT( 15 | addr : IN std_logic_vector(15 downto 0); 16 | data : INOUT std_logic_vector(7 downto 0); 17 | n_dma : OUT std_logic; 18 | ba : IN std_logic; 19 | n_roml : IN std_logic; 20 | n_romh : IN std_logic; 21 | n_io1 : IN std_logic; 22 | n_io2 : IN std_logic; 23 | n_wr : IN std_logic; 24 | n_irq : OUT std_logic; 25 | n_nmi : OUT std_logic; 26 | n_reset : IN std_logic; 27 | d_clk : IN std_logic; 28 | clk : IN std_logic; 29 | phi2 : IN std_logic; 30 | n_exrom : OUT std_logic; 31 | n_game : OUT std_logic; 32 | n_sw2 : IN std_logic; 33 | n_led : OUT std_logic; 34 | sa : IN std_logic_vector (5 downto 0); 35 | sd : OUT std_logic_vector (15 downto 0); 36 | sdrr : OUT std_logic; 37 | sdwr : OUT std_logic; 38 | n_soe : IN std_logic; 39 | n_swe : IN std_logic; 40 | gpio : IN std_logic_vector (27 downto 26); 41 | n_flash_cs : OUT std_logic; 42 | n_ram_cs : OUT std_logic; 43 | sck : OUT std_logic; 44 | miso : OUT std_logic; 45 | n_wp : OUT std_logic; 46 | mosi : OUT std_logic; 47 | n_hold : OUT std_logic; 48 | tp1 : OUT std_logic; 49 | tp2 : OUT std_logic; 50 | tp3 : OUT std_logic; 51 | tp4 : OUT std_logic; 52 | tp5 : OUT std_logic 53 | ); 54 | END COMPONENT; 55 | 56 | --Inputs 57 | signal addr : std_logic_vector(15 downto 0) := (others => '0'); 58 | signal ba : std_logic := '1'; 59 | signal n_roml : std_logic := '1'; 60 | signal n_romh : std_logic := '1'; 61 | signal n_io1 : std_logic := '1'; 62 | signal n_io2 : std_logic := '1'; 63 | signal n_wr : std_logic := '1'; 64 | signal n_reset : std_logic := '0'; 65 | signal d_clk : std_logic := '0'; 66 | signal clk : std_logic := '0'; 67 | signal phi2 : std_logic := '0'; 68 | signal n_sw2 : std_logic := '1'; 69 | signal sa : std_logic_vector(5 downto 0) := (others => '1'); 70 | signal n_soe : std_logic := '1'; 71 | signal n_swe : std_logic := '1'; 72 | signal gpio : std_logic_vector(27 downto 26) := (others => '1'); 73 | 74 | --BiDirs 75 | signal data : std_logic_vector(7 downto 0); 76 | 77 | --Outputs 78 | signal n_dma : std_logic; 79 | signal n_irq : std_logic; 80 | signal n_nmi : std_logic; 81 | signal n_exrom : std_logic; 82 | signal n_game : std_logic; 83 | signal n_led : std_logic; 84 | signal sd : std_logic_vector(15 downto 0); 85 | signal sdrr : std_logic; 86 | signal sdwr : std_logic; 87 | signal n_flash_cs : std_logic; 88 | signal n_ram_cs : std_logic; 89 | signal sck : std_logic; 90 | signal miso : std_logic; 91 | signal n_wp : std_logic; 92 | signal mosi : std_logic; 93 | signal n_hold : std_logic; 94 | signal tp1 : std_logic; 95 | signal tp2 : std_logic; 96 | signal tp3 : std_logic; 97 | signal tp4 : std_logic; 98 | signal tp5 : std_logic; 99 | 100 | -- Internal signals 101 | signal smi_clk : std_logic; 102 | signal last_sdrr : std_logic := '0'; 103 | 104 | -- Clock period definitions 105 | constant clk_period : time := 40 ns; 106 | constant d_clk_period : time := 126.8716 ns; 107 | constant phi2_period : time := d_clk_period * 8; 108 | constant smi_clk_period : time := 4 ns; 109 | 110 | -- MOS6510 clock timing 111 | constant t_aes_max : time := 75 ns; 112 | constant t_hrw_typ : time := 30 ns; -- Same as t_ha 113 | 114 | BEGIN 115 | uut: cpi PORT MAP ( 116 | addr => addr, 117 | data => data, 118 | n_dma => n_dma, 119 | ba => ba, 120 | n_roml => n_roml, 121 | n_romh => n_romh, 122 | n_io1 => n_io1, 123 | n_io2 => n_io2, 124 | n_wr => n_wr, 125 | n_irq => n_irq, 126 | n_nmi => n_nmi, 127 | n_reset => n_reset, 128 | d_clk => d_clk, 129 | clk => clk, 130 | phi2 => phi2, 131 | n_exrom => n_exrom, 132 | n_game => n_game, 133 | n_sw2 => n_sw2, 134 | n_led => n_led, 135 | sa => sa, 136 | sd => sd, 137 | sdrr => sdrr, 138 | sdwr => sdwr, 139 | n_soe => n_soe, 140 | n_swe => n_swe, 141 | gpio => gpio, 142 | n_flash_cs => n_flash_cs, 143 | n_ram_cs => n_ram_cs, 144 | sck => sck, 145 | miso => miso, 146 | n_wp => n_wp, 147 | mosi => mosi, 148 | n_hold => n_hold, 149 | tp1 => tp1, 150 | tp2 => tp2, 151 | tp3 => tp3, 152 | tp4 => tp4, 153 | tp5 => tp5 154 | ); 155 | 156 | -- Clock process definitions 157 | clk_process :process 158 | begin 159 | clk <= '0'; 160 | wait for clk_period/2; 161 | clk <= '1'; 162 | wait for clk_period/2; 163 | end process; 164 | 165 | d_clk_process :process 166 | begin 167 | d_clk <= '1'; 168 | wait for d_clk_period/2; 169 | d_clk <= '0'; 170 | wait for d_clk_period/2; 171 | end process; 172 | 173 | phi2_process :process 174 | begin 175 | phi2 <= '0'; 176 | wait for phi2_period/2; 177 | phi2 <= '1'; 178 | wait for phi2_period/2; 179 | end process; 180 | 181 | smi_clk_process :process 182 | begin 183 | smi_clk <= '0'; 184 | wait for smi_clk_period/2; 185 | smi_clk <= '1'; 186 | wait for smi_clk_period/2; 187 | end process; 188 | 189 | -- SMI read 190 | smi_read_process :process 191 | begin 192 | n_soe <= '1'; 193 | wait until rising_edge(smi_clk); 194 | if sdrr = '1' then 195 | if last_sdrr = '0' then 196 | last_sdrr <= '1'; 197 | wait for 30 ns; 198 | end if; 199 | wait for 30 ns; 200 | n_soe <= '0'; 201 | wait for 60 ns; 202 | n_soe <= '1'; 203 | wait for 30 ns; 204 | else 205 | last_sdrr <= '0'; 206 | end if; 207 | end process; 208 | 209 | -- Stimulus process 210 | stim_proc: process 211 | begin 212 | -- hold reset state for 100 ns. 213 | wait for 100 ns; 214 | n_reset <= '0'; 215 | data <= (others => 'Z'); 216 | 217 | wait for 20 ns; 218 | n_reset <= '1'; 219 | 220 | -- write cycle 221 | wait until rising_edge(phi2); 222 | wait for t_aes_max; 223 | n_wr <= '0'; 224 | addr <= x"de00"; 225 | data <= x"64"; 226 | n_io1 <= '0'; -- TODO: add delay to n_io? 227 | wait until falling_edge(phi2); 228 | wait for t_hrw_typ; 229 | n_wr <= '1'; 230 | addr <= x"0000"; 231 | data <= (others => 'Z'); 232 | n_io1 <= '1'; 233 | 234 | -- read cycle (n_io1) 235 | wait until rising_edge(phi2); 236 | wait for t_aes_max; 237 | n_wr <= '1'; 238 | addr <= x"deff"; 239 | data <= x"64"; 240 | n_io1 <= '0'; 241 | -- wait for t_acc_max; 242 | wait until falling_edge(phi2); 243 | wait for t_hrw_typ; 244 | n_wr <= '1'; 245 | addr <= x"0000"; 246 | data <= (others => 'Z'); 247 | n_io1 <= '1'; 248 | 249 | -- test switch 250 | wait for 50 ns; 251 | n_sw2 <= '0'; 252 | 253 | -- read cycle (n_io2) 254 | wait until rising_edge(phi2); 255 | wait for t_aes_max; 256 | n_wr <= '1'; 257 | addr <= x"df5a"; 258 | n_io2 <= '0'; 259 | -- wait for t_acc_max; 260 | wait until falling_edge(phi2); 261 | wait for t_hrw_typ; 262 | addr <= x"0000"; 263 | n_io2 <= '1'; 264 | 265 | wait; 266 | end process; 267 | END; 268 | -------------------------------------------------------------------------------- /vhdl/test_bench.cmd: -------------------------------------------------------------------------------- 1 | wave add -radix hex test_bench 2 | run 4 us 3 | wcfg open ../test_bench.wcfg 4 | 5 | -------------------------------------------------------------------------------- /vhdl/test_bench.prj: -------------------------------------------------------------------------------- 1 | vhdl work "src/exp_bus_ctrl.vhdl" 2 | vhdl work "src/cpi.vhdl" 3 | vhdl work "src/test_bench.vhd" 4 | -------------------------------------------------------------------------------- /vhdl/test_bench.wcfg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | clk 17 | clk 18 | HEXRADIX 19 | 20 | 21 | d_clk 22 | d_clk 23 | HEXRADIX 24 | 25 | 26 | phi2 27 | phi2 28 | HEXRADIX 29 | 30 | 31 | addr[15:0] 32 | addr[15:0] 33 | HEXRADIX 34 | 35 | 36 | n_wr 37 | n_wr 38 | HEXRADIX 39 | 40 | 41 | data[7:0] 42 | data[7:0] 43 | HEXRADIX 44 | 45 | 46 | ba 47 | ba 48 | HEXRADIX 49 | 50 | 51 | n_roml 52 | n_roml 53 | HEXRADIX 54 | 55 | 56 | n_romh 57 | n_romh 58 | HEXRADIX 59 | 60 | 61 | n_io1 62 | n_io1 63 | HEXRADIX 64 | 65 | 66 | n_io2 67 | n_io2 68 | HEXRADIX 69 | 70 | 71 | n_reset 72 | n_reset 73 | HEXRADIX 74 | 75 | 76 | sa[5:0] 77 | sa[5:0] 78 | HEXRADIX 79 | 80 | 81 | n_swe 82 | n_swe 83 | HEXRADIX 84 | 85 | 86 | n_soe 87 | n_soe 88 | HEXRADIX 89 | 90 | 91 | sd[15:0] 92 | sd[15:0] 93 | HEXRADIX 94 | 95 | 96 | sdrr 97 | sdrr 98 | HEXRADIX 99 | 100 | 101 | sdwr 102 | sdwr 103 | HEXRADIX 104 | 105 | 106 | n_sw2 107 | n_sw2 108 | HEXRADIX 109 | 110 | 111 | n_led 112 | n_led 113 | HEXRADIX 114 | 115 | 116 | tp1 117 | tp1 118 | HEXRADIX 119 | 120 | 121 | tp2 122 | tp2 123 | HEXRADIX 124 | 125 | 126 | tp3 127 | tp3 128 | HEXRADIX 129 | 130 | 131 | tp4 132 | tp4 133 | HEXRADIX 134 | 135 | 136 | tp5 137 | tp5 138 | HEXRADIX 139 | 140 | 141 | n_dma 142 | n_dma 143 | HEXRADIX 144 | 145 | 146 | n_irq 147 | n_irq 148 | HEXRADIX 149 | 150 | 151 | n_nmi 152 | n_nmi 153 | HEXRADIX 154 | 155 | 156 | n_exrom 157 | n_exrom 158 | HEXRADIX 159 | 160 | 161 | n_game 162 | n_game 163 | HEXRADIX 164 | 165 | 166 | n_flash_cs 167 | n_flash_cs 168 | HEXRADIX 169 | 170 | 171 | n_ram_cs 172 | n_ram_cs 173 | HEXRADIX 174 | 175 | 176 | sck 177 | sck 178 | HEXRADIX 179 | 180 | 181 | miso 182 | miso 183 | HEXRADIX 184 | 185 | 186 | n_wp 187 | n_wp 188 | HEXRADIX 189 | 190 | 191 | mosi 192 | mosi 193 | HEXRADIX 194 | 195 | 196 | n_hold 197 | n_hold 198 | HEXRADIX 199 | 200 | 201 | clk_period 202 | clk_period 203 | HEXRADIX 204 | 205 | 206 | d_clk_period 207 | d_clk_period 208 | HEXRADIX 209 | 210 | 211 | phi2_period 212 | phi2_period 213 | HEXRADIX 214 | 215 | 216 | t_aes_max 217 | t_aes_max 218 | HEXRADIX 219 | 220 | 221 | t_hrw_typ 222 | t_hrw_typ 223 | HEXRADIX 224 | 225 | 226 | --------------------------------------------------------------------------------