├── CLK28MPLL.vhd ├── DE1_TOP.vhd ├── DE2_TOP.vhd ├── Makefile ├── PS2_Ctrl.vhd ├── README ├── apple2.vhd ├── apple2fpga_DE1.qpf ├── apple2fpga_DE2.qpf ├── apple_II.rom ├── bios.a65 ├── bios.rom ├── character_rom.vhd ├── cpu6502.vhd ├── disk2_spi_slave.vhd ├── disk_ii.vhd ├── disk_ii_rom.vhd ├── dos33master.nib ├── dsk2nib.c ├── dsk2nib.sln ├── dsk2nib.vcproj ├── esp32 ├── linux_keyboard.py ├── main.py ├── osd.py ├── ps2.py ├── ps2recv.py ├── ps2tn.py ├── pygame_keyboard_mouse.py └── v31x │ ├── disk2.py │ ├── ps2.py │ ├── ps2recv.py │ └── pygame_keyboard_mouse.py ├── i2c_controller.vhd ├── i2c_testbench.vhd ├── keyboard.vhd ├── main_roms.vhd ├── makenibs ├── paddle.vhd ├── proj └── lattice │ ├── constraints │ ├── FFM-LFE5U-V0r0_mit_FFC-CA7-V2r0.lpf │ ├── ulx3s_v20_difgpdi.lpf │ └── ulx3s_v31.lpf │ ├── ffmlfe5 │ └── makefile │ ├── scripts │ ├── IspXCF.dtd │ ├── diamond_main.mk │ ├── diamond_path.mk │ ├── ecp5-ocd.sh │ ├── ft2232.ocd │ ├── ft231x.ocd │ ├── ft231x2.ocd │ ├── ft232r.ocd │ ├── ft4232.ocd │ ├── ldf.xsl │ ├── project.ldf │ ├── trellis_main.mk │ ├── trellis_main_ghdl.mk │ ├── trellis_path.mk │ ├── ulx3s.sty │ ├── ulx3s_flash_is25lp032d.xcf │ ├── ulx3s_flash_is25lp128f.xcf │ ├── ulx3s_flash_s25fl164k.xcf │ ├── ulx3s_sram.xcf │ └── xcf.xsl │ └── ulx3s │ ├── Makefile │ ├── README.md │ ├── clocks │ └── clk_25_shift_pixel.vhd │ ├── font_bizcat8x16.mem │ ├── osd.mem │ └── osd.txt ├── rom2vhdl ├── rtl_emard ├── bram │ └── bram_true2p_1clk.vhd ├── dvi │ ├── tmds_encoder.vhd │ └── vga2dvid.vhd ├── lattice │ ├── ecp5 │ │ └── clocks │ │ │ ├── clk_sys_vhdl.vhd │ │ │ └── ecp5pll.vhd │ └── top │ │ ├── ffmlfe5_apple2.vhd │ │ ├── ulx3s_v20_apple2.vhd │ │ └── ulx3s_v31_apple2.vhd ├── oled │ ├── oled_font_pack.vhd │ ├── oled_hex_decoder.vhd │ └── oled_init_pack.vhd ├── osd │ ├── font2readmemb.py │ ├── font_bizcat8x16.mem │ ├── font_bizcat8x16.raw │ ├── osd.v │ ├── osd_vhd.vhd │ ├── spi_osd.vhd │ ├── spi_osd_v.v │ ├── spi_ram_btn.vhd │ ├── spi_ram_btn_v.v │ ├── spirw_slave.vhd │ └── spirw_slave_v.v ├── spi_display │ ├── spi_display.vhd │ ├── spi_display_init_pack.vhd │ └── st7789_init_pack.vhd └── usb │ ├── usb11_phy_vhdl │ ├── usb_phy.vhd │ ├── usb_phy_transciver.vhd │ ├── usb_rx_phy.vhd │ └── usb_tx_phy.vhd │ ├── usbhid │ ├── report_decoded_pack_generic.vhd │ ├── usbhid_report_decoder_darfon_joystick.vhd │ ├── usbhid_report_decoder_logitech_mouse.vhd │ ├── usbhid_report_decoder_nes_joystick.vhd │ ├── usbhid_report_decoder_saitek_joystick.vhd │ └── usbhid_report_decoder_xbox360_joystick.vhd │ └── usbhost │ ├── usbh_crc16.v │ ├── usbh_crc5.v │ ├── usbh_host_hid.vhd │ ├── usbh_setup_pack.vhd │ ├── usbh_sie.v │ └── usbh_sie_vhdl.vhd ├── slot6.rom ├── spi_controller.vhd ├── timing_generator.vhd ├── timing_testbench.vhd ├── vga_controller.vhd ├── video_generator.vhd └── wm8731_audio.vhd /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for the Apple ][-on-an-FPGA project 2 | # 3 | # Mostly creates an archive, prepares the ROM images, and run test benches 4 | # 5 | # DOES NOT compile the VHDL for the FPGA. This is done within Quartus. 6 | # 7 | # Stephen A. Edwards, Columbia University, sedwards@cs.columbia.edu 8 | 9 | VERSION = 1.2 10 | NAME = apple2fpga-$(VERSION) 11 | 12 | ZIPFILES = \ 13 | README \ 14 | Makefile \ 15 | DE1_TOP.qsf \ 16 | DE2_TOP.qsf \ 17 | apple2fpga_DE1.qpf \ 18 | apple2fpga_DE2.qpf \ 19 | apple2.vhd \ 20 | character_rom.vhd \ 21 | CLK28MPLL.vhd \ 22 | CLK28MPLL.qip \ 23 | cpu6502.vhd \ 24 | DE1_TOP.vhd \ 25 | DE2_TOP.vhd \ 26 | disk_ii.vhd \ 27 | i2c_controller.vhd \ 28 | keyboard.vhd \ 29 | PS2_Ctrl.vhd \ 30 | spi_controller.vhd \ 31 | timing_generator.vhd \ 32 | vga_controller.vhd \ 33 | video_generator.vhd \ 34 | wm8731_audio.vhd \ 35 | disk_ii_rom.vhd \ 36 | main_roms.vhd \ 37 | i2c_testbench.vhd \ 38 | timing_testbench.vhd \ 39 | dos33master.nib \ 40 | dsk2nib.c \ 41 | dsk2nib.sln \ 42 | dsk2nib.vcproj \ 43 | rom2vhdl \ 44 | makenibs \ 45 | bios.a65 \ 46 | bios.rom \ 47 | DE1_TOP.sof \ 48 | DE2_TOP.sof \ 49 | apple_II.rom \ 50 | slot6.rom 51 | 52 | ############################## 53 | # Create the .zip file archive 54 | 55 | $(NAME).zip : $(ZIPFILES) 56 | zip $(NAME).zip $(ZIPFILES) 57 | 58 | ############################## 59 | # Create the two VHDL files for the actual ROMS 60 | 61 | # apple_II.rom should be a 12287-byte file that represents the contents 62 | # of the Apple II's ROMS, i.e., memory from 0xd000 to 0xffff 63 | 64 | main_roms.vhd : apple_II.rom 65 | ./rom2vhdl main_roms 13 12287 < apple_II.rom > main_roms.vhd 66 | 67 | # slot6.rom should be a 256-byte file that represents the contents of the 68 | # Disk II controller card. When in slot 6, it appears in memory from 69 | # 0xc600 to 0xc6FF 70 | 71 | disk_ii_rom.vhd : slot6.rom 72 | ./rom2vhdl disk_ii_rom 7 255 < slot6.rom > disk_ii_rom.vhd 73 | 74 | ############################## 75 | # Assemble the "fake BIOS" using the xa65 cross-assembler 76 | 77 | bios.rom : bios.a65 78 | xa -bt 53248 -A 53248 -o bios.rom -l bios.labels bios.a65 79 | 80 | # Disassemble the "fake BIOS" to 81 | 82 | bios.dis : bios.rom 83 | dxa -g d000 -a dump -r d000 bios.rom > bios.dis 84 | 85 | ############################## 86 | # Rules for running the various testbenches using ghdl, the GNU VHDL simulator 87 | 88 | VHDL_SRC = timing_generator.vhd \ 89 | timing_testbench.vhd \ 90 | i2c_controller.vhd \ 91 | i2c_testbench.vhd 92 | 93 | TIMING = 10000000ns 94 | 95 | timing_testbench : $(VHDL_SRC:%.vhd=%.o) 96 | ghdl -e timing_testbench 97 | 98 | timing_testbench.o : timing_generator.o 99 | 100 | # Run the timing testbench to generate a .vcd file, which can be viewed with 101 | # 102 | # gtkwave timing_testbench.vcd timing_testbench.sav 103 | # 104 | timing_testbench.vcd : timing_testbench Makefile 105 | -./timing_testbench --vcd=timing_testbench.vcd --stop-time=$(TIMING) 2> timing_testbench.log 106 | 107 | i2c_testbench : $(VHDL_SRC:%.vhd=%.o) 108 | ghdl -e i2c_testbench 109 | 110 | i2c_testbench.o : i2c_controller.o 111 | 112 | # Run the i2c testbench to generate a .vcd file, which can be viewed with 113 | # 114 | # gtkwave i2c_testbench.vcd i2c_testbench.sav 115 | # 116 | i2c_testbench.vcd : i2c_testbench Makefile 117 | ./i2c_testbench --vcd=i2c_testbench.vcd --stop-time=$(TIMING) 2> i2c_testbench.log 118 | 119 | %.o : %.vhd 120 | ghdl -a $< 121 | -------------------------------------------------------------------------------- /PS2_Ctrl.vhd: -------------------------------------------------------------------------------- 1 | -- PS2_Ctrl.vhd 2 | -- ------------------------------------------------ 3 | -- Simplified PS/2 Controller (kbd, mouse...) 4 | -- ------------------------------------------------ 5 | -- Only the Receive function is implemented ! 6 | -- (c) ALSE. http://www.alse-fr.com 7 | 8 | library IEEE; 9 | use IEEE.Std_Logic_1164.all; 10 | use IEEE.Numeric_Std.all; 11 | 12 | -- -------------------------------------- 13 | Entity PS2_Ctrl is 14 | -- -------------------------------------- 15 | generic (FilterSize : positive := 8); 16 | port( Clk : in std_logic; -- System Clock 17 | Reset : in std_logic; -- System Reset 18 | PS2_Clk : in std_logic; -- Keyboard Clock Line 19 | PS2_Data : in std_logic; -- Keyboard Data Line 20 | DoRead : in std_logic; -- From outside when reading the scan code 21 | Scan_Err : out std_logic; -- To outside : Parity or Overflow error 22 | Scan_DAV : out std_logic; -- To outside when a scan code has arrived 23 | Scan_Code : out unsigned(7 downto 0) -- Eight bits Data Out 24 | ); 25 | end PS2_Ctrl; 26 | 27 | -- -------------------------------------- 28 | Architecture ALSE_RTL of PS2_Ctrl is 29 | -- -------------------------------------- 30 | -- (c) ALSE. http://www.alse-fr.com 31 | -- Author : Bert Cuzeau. 32 | -- Fully synchronous solution, same Filter on PS2_Clk. 33 | -- Still as compact as "Plain_wrong"... 34 | -- Possible improvement : add TIMEOUT on PS2_Clk while shifting 35 | -- Note: PS2_Data is resynchronized though this should not be 36 | -- necessary (qualified by Fall_Clk and does not change at that time). 37 | -- Note the tricks to correctly interpret 'H' as '1' in RTL simulation. 38 | 39 | signal PS2_Datr : std_logic; 40 | 41 | subtype Filter_t is std_logic_vector(FilterSize-1 downto 0); 42 | signal Filter : Filter_t; 43 | signal Fall_Clk : std_logic; 44 | signal Bit_Cnt : unsigned(3 downto 0); 45 | signal Parity : std_logic; 46 | signal Scan_DAVi : std_logic; 47 | 48 | signal S_Reg : unsigned(8 downto 0); 49 | 50 | signal PS2_Clk_f : std_logic; 51 | 52 | Type State_t is (Idle, Shifting); 53 | signal State : State_t; 54 | 55 | begin 56 | 57 | Scan_DAV <= Scan_DAVi; 58 | 59 | -- This filters digitally the raw clock signal coming from the keyboard : 60 | -- * Eight consecutive PS2_Clk=1 makes the filtered_clock go high 61 | -- * Eight consecutive PS2_Clk=0 makes the filtered_clock go low 62 | -- Implies a (FilterSize+1) x Tsys_clock delay on Fall_Clk wrt Data 63 | -- Also in charge of the re-synchronization of PS2_Data 64 | 65 | process (Clk,Reset) 66 | begin 67 | if Reset='1' then 68 | PS2_Datr <= '0'; 69 | PS2_Clk_f <= '0'; 70 | Filter <= (others=>'0'); 71 | Fall_Clk <= '0'; 72 | elsif rising_edge (Clk) then 73 | PS2_Datr <= PS2_Data and PS2_Data; -- also turns 'H' into '1' 74 | Fall_Clk <= '0'; 75 | Filter <= (PS2_Clk and PS2_CLK) & Filter(Filter'high downto 1); 76 | if Filter = Filter_t'(others=>'1') then 77 | PS2_Clk_f <= '1'; 78 | elsif Filter = Filter_t'(others=>'0') then 79 | PS2_Clk_f <= '0'; 80 | if PS2_Clk_f = '1' then 81 | Fall_Clk <= '1'; 82 | end if; 83 | end if; 84 | end if; 85 | end process; 86 | 87 | 88 | -- This simple State Machine reads in the Serial Data 89 | -- coming from the PS/2 peripheral. 90 | 91 | process(Clk,Reset) 92 | begin 93 | 94 | if Reset='1' then 95 | State <= Idle; 96 | Bit_Cnt <= (others => '0'); 97 | S_Reg <= (others => '0'); 98 | Scan_Code <= (others => '0'); 99 | Parity <= '0'; 100 | Scan_Davi <= '0'; 101 | Scan_Err <= '0'; 102 | 103 | elsif rising_edge (Clk) then 104 | 105 | if DoRead='1' then 106 | Scan_Davi <= '0'; -- note: this assgnmnt can be overriden 107 | end if; 108 | 109 | case State is 110 | 111 | when Idle => 112 | Parity <= '0'; 113 | Bit_Cnt <= (others => '0'); 114 | -- note that we dont need to clear the Shift Register 115 | if Fall_Clk='1' and PS2_Datr='0' then -- Start bit 116 | Scan_Err <= '0'; 117 | State <= Shifting; 118 | end if; 119 | 120 | when Shifting => 121 | if Bit_Cnt >= 9 then 122 | if Fall_Clk='1' then -- Stop Bit 123 | -- Error is (wrong Parity) or (Stop='0') or Overflow 124 | Scan_Err <= (not Parity) or (not PS2_Datr) or Scan_DAVi; 125 | Scan_Davi <= '1'; 126 | Scan_Code <= S_Reg(7 downto 0); 127 | State <= Idle; 128 | end if; 129 | elsif Fall_Clk='1' then 130 | Bit_Cnt <= Bit_Cnt + 1; 131 | S_Reg <= PS2_Datr & S_Reg (S_Reg'high downto 1); -- Shift right 132 | Parity <= Parity xor PS2_Datr; 133 | end if; 134 | 135 | when others => -- never reached 136 | State <= Idle; 137 | 138 | end case; 139 | 140 | --Scan_Err <= '0'; -- to create an on-purpose error on Scan_Err ! 141 | 142 | end if; 143 | 144 | end process; 145 | 146 | end ALSE_RTL; 147 | 148 | -------------------------------------------------------------------------------- /apple2fpga_DE1.qpf: -------------------------------------------------------------------------------- 1 | # -------------------------------------------------------------------------- # 2 | # 3 | # Copyright (C) 1991-2009 Altera Corporation 4 | # Your use of Altera Corporation's design tools, logic functions 5 | # and other software and tools, and its AMPP partner logic 6 | # functions, and any output files from any of the foregoing 7 | # (including device programming or simulation files), and any 8 | # associated documentation or information are expressly subject 9 | # to the terms and conditions of the Altera Program License 10 | # Subscription Agreement, Altera MegaCore Function License 11 | # Agreement, or other applicable license agreement, including, 12 | # without limitation, that your use is for the sole purpose of 13 | # programming logic devices manufactured by Altera and sold by 14 | # Altera or its authorized distributors. Please refer to the 15 | # applicable agreement for further details. 16 | # 17 | # -------------------------------------------------------------------------- # 18 | # 19 | # Quartus II 20 | # Version 9.1 Build 222 10/21/2009 SJ Web Edition 21 | # Date created = 23:32:35 December 07, 2009 22 | # 23 | # -------------------------------------------------------------------------- # 24 | 25 | QUARTUS_VERSION = "9.1" 26 | DATE = "23:32:35 December 07, 2009" 27 | 28 | # Revisions 29 | 30 | PROJECT_REVISION = "DE1_TOP" 31 | -------------------------------------------------------------------------------- /apple2fpga_DE2.qpf: -------------------------------------------------------------------------------- 1 | # Copyright (C) 1991-2007 Altera Corporation 2 | # Your use of Altera Corporation's design tools, logic functions 3 | # and other software and tools, and its AMPP partner logic 4 | # functions, and any output files from any of the foregoing 5 | # (including device programming or simulation files), and any 6 | # associated documentation or information are expressly subject 7 | # to the terms and conditions of the Altera Program License 8 | # Subscription Agreement, Altera MegaCore Function License 9 | # Agreement, or other applicable license agreement, including, 10 | # without limitation, that your use is for the sole purpose of 11 | # programming logic devices manufactured by Altera and sold by 12 | # Altera or its authorized distributors. Please refer to the 13 | # applicable agreement for further details. 14 | 15 | 16 | 17 | QUARTUS_VERSION = "7.2" 18 | DATE = "17:25:25 January 26, 2008" 19 | 20 | 21 | # Revisions 22 | 23 | PROJECT_REVISION = "DE2_TOP" 24 | -------------------------------------------------------------------------------- /apple_II.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emard/apple2fpga/0ad84ae2f9d9d5cc0e8db6128d630a9e16cdd6de/apple_II.rom -------------------------------------------------------------------------------- /bios.a65: -------------------------------------------------------------------------------- 1 | ; Trivial BIOS for an Apple II+ -- just enough to show the hardware works 2 | ; 3 | ; Stephen A. Edwards 4 | ; 5 | ; Assemble with "xa -A 53248 -bt 53248 ..." to start the code at $D000 6 | 7 | gbasl = $26 8 | gbash = $27 9 | basl = $28 10 | bash = $29 11 | 12 | text = $400 ; start of text/lores screen memory 13 | bhires = $2000 ; start of hires screen memory 14 | line8 = $428 15 | line9 = $4A8 16 | line10 = $528 17 | line11 = $5A8 18 | 19 | line23 = $7D0 20 | 21 | keyboard = $C000 22 | key_strobe = $C010 23 | 24 | spkr = $C030 25 | grset = $C050 26 | txtset = $C051 27 | nomix = $C052 28 | mix = $C053 29 | page1 = $C054 30 | page2 = $C055 31 | lores = $C056 32 | hires = $C057 33 | 34 | nmi_vector = $FFFA 35 | nmi = reset 36 | irq = reset 37 | 38 | reset: 39 | cld 40 | lda txtset 41 | lda page1 42 | 43 | ; Clear the text screen 44 | 45 | lda #text 48 | sta gbash 49 | ldy #0 50 | ldx #4 51 | lda #$A0 ; Normal space 52 | l0 53 | sta (gbasl),y 54 | dey 55 | bne l0 56 | inc gbash 57 | dex 58 | bne l0 59 | 60 | ; Play three tones 61 | 62 | ldy #0 63 | tone1 64 | lda #15 65 | jsr wait 66 | sta spkr 67 | dey 68 | bne tone1 69 | 70 | tone2 71 | lda #12 72 | jsr wait 73 | sta spkr 74 | dey 75 | bne tone2 76 | 77 | tone3 78 | lda #8 79 | jsr wait 80 | sta spkr 81 | dey 82 | bne tone3 83 | 84 | ; Print some messages 85 | lda #text 88 | sta gbash 89 | lda #hello 92 | sta bash 93 | jsr write_string 94 | 95 | lda #roms1 100 | sta bash 101 | jsr write_string 102 | 103 | lda #line10 106 | sta gbash 107 | lda #roms2 110 | sta bash 111 | jsr write_string 112 | 113 | lda #line11 116 | sta gbash 117 | lda #roms3 120 | sta bash 121 | jsr write_string 122 | 123 | lda #line23 126 | sta gbash 127 | lda #press 130 | sta bash 131 | jsr write_string 132 | 133 | wait_for_key 134 | bit keyboard 135 | bpl wait_for_key 136 | sta key_strobe 137 | 138 | ; Set lores mode 139 | sta lores 140 | sta nomix 141 | sta grset 142 | 143 | ; Fill the screen with colors 144 | lda #0 145 | fill 146 | ldx #text 149 | stx gbash 150 | ldy #0 151 | ldx #4 152 | l2 153 | sta (gbasl),y 154 | adc #1 155 | dey 156 | bne l2 157 | inc gbash 158 | dex 159 | bne l2 160 | 161 | adc #1 162 | 163 | bit keyboard 164 | bpl fill 165 | sta key_strobe 166 | 167 | ; Wait for another keypress 168 | 169 | wait_key1 170 | bit keyboard 171 | bpl wait_key1 172 | sta key_strobe 173 | 174 | ; Set hires mode 175 | sta hires 176 | 177 | ; Fill the screen with colors 178 | lda #0 179 | fillh 180 | ldx #bhires 183 | stx gbash 184 | ldy #0 185 | ldx #$20 186 | l3 187 | sta (gbasl),y 188 | adc #1 189 | dey 190 | bne l3 191 | inc gbash 192 | dex 193 | bne l3 194 | 195 | adc #1 196 | 197 | bit keyboard 198 | bpl fillh 199 | sta key_strobe 200 | 201 | ; Wait for another keypress 202 | 203 | wait_key2 204 | bit keyboard 205 | bpl wait_key2 206 | sta key_strobe 207 | 208 | jmp reset 209 | 210 | done 211 | rts 212 | write_string 213 | ldy #0 214 | l1 215 | lda (basl),y 216 | beq done 217 | ora #$80 218 | sta (gbasl),y 219 | iny 220 | bne l1 221 | rts 222 | 223 | ; A quadratic delay 224 | 225 | wait sec 226 | wait2 pha 227 | wait3 sbc #1 228 | bne wait3 229 | pla 230 | sbc #1 231 | bne wait2 232 | rts 233 | 234 | 235 | hello .asc "APPLE2FPGA" 236 | .byt 0 237 | 238 | roms1 .asc "THIS IS NOT APPLE'S BIOS" 239 | .byt 0 240 | 241 | roms2 .asc "TO RUN APPLE PROGRAMS, GET A COPY OF THE" 242 | .byt 0 243 | 244 | roms3 .asc "ACTUAL APPLE II ROMS (D000-FFFF)" 245 | .byt 0 246 | 247 | press .asc "PRESS ANY KEY TO ADVANCE" 248 | .byt 0 249 | 250 | ; Pad to the start of the NMI vector 251 | here .dsb (nmi_vector - here) 252 | 253 | .word nmi 254 | .word reset 255 | .word irq 256 | -------------------------------------------------------------------------------- /bios.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emard/apple2fpga/0ad84ae2f9d9d5cc0e8db6128d630a9e16cdd6de/bios.rom -------------------------------------------------------------------------------- /disk2_spi_slave.vhd: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | -- 3 | -- SPI slave for DISK ][ 4 | -- 5 | -- AUTHOR=EMARD 6 | -- LICENSE=BSD 7 | -- 8 | ------------------------------------------------------------------------------- 9 | 10 | library ieee; 11 | use ieee.std_logic_1164.all; 12 | use ieee.numeric_std.all; 13 | 14 | entity disk2_spi_slave is 15 | generic 16 | ( 17 | C_track_len : natural := 6656 -- bytes 18 | ); 19 | port 20 | ( 21 | -- System Interface ------------------------------------------------------- 22 | CLK_14M : in std_logic; -- System clock 23 | reset : in std_logic; 24 | -- SPI slave Interface ---------------------------------------------------- 25 | CS : in std_logic; -- slave chip select 26 | SCLK : in std_logic; -- SPI clock 27 | MOSI : in std_logic; -- Data from ESP32 (master out slave in) 28 | MISO : inout std_logic; -- Data to ESP32 (master in slave out) 29 | -- Track to read, requested by APPLE ][ 30 | track : in unsigned(5 downto 0); -- Track number (0-34) 31 | -- IRQ for ESP32 to change to new track 32 | track_change : out std_logic; 33 | -- Write to Track buffer, APPLE ][ is circular reading this --------------- 34 | ram_write_addr : out unsigned(13 downto 0); 35 | ram_di : out unsigned(7 downto 0); 36 | ram_we : out std_logic 37 | ); 38 | end; 39 | 40 | architecture rtl of disk2_spi_slave is 41 | signal R_MISO_shift, R_MOSI_shift, S_MOSI_shift_next: unsigned(7 downto 0); 42 | signal R_SCLK_shift : std_logic_vector(1 downto 0); 43 | signal R_track: unsigned(track'range); 44 | signal R_track_change: std_logic := '0'; 45 | signal R_bit_counter: unsigned(2 downto 0); 46 | signal R_ram_we: std_logic; 47 | signal R_ram_di : unsigned(7 downto 0); 48 | signal R_ram_write_addr: unsigned(ram_write_addr'range); 49 | begin 50 | -- SPI clock Edge detection shift right and track change detection 51 | P_SCLK_shift_tracker: process(CLK_14M) 52 | begin 53 | if rising_edge(CLK_14M) then 54 | R_SCLK_shift <= SCLK & R_SCLK_shift(1); 55 | if track /= R_track then 56 | R_track_change <= '1'; 57 | else 58 | R_track_change <= '0'; 59 | end if; 60 | R_track <= track; 61 | end if; 62 | end process P_SCLK_shift_tracker; 63 | track_change <= R_track_change; 64 | 65 | P_SPI_slave: process(CLK_14M) 66 | begin 67 | if rising_edge(CLK_14M) then 68 | if R_track_change = '1' or CS = '0' then -- track change or CS = 0 resets state 69 | R_MISO_shift <= "00" & R_track; 70 | R_bit_counter <= (others => '0'); 71 | R_ram_write_addr <= to_unsigned(C_track_len-1, R_ram_write_addr'length); 72 | R_ram_we <= '0'; 73 | else 74 | if R_SCLK_shift = "10" then -- SCLK rising edge 75 | R_MOSI_shift <= R_MOSI_shift(R_MOSI_shift'high-1 downto 0) & MOSI; 76 | R_MISO_shift <= R_MISO_shift(R_MISO_shift'high-1 downto 0) & R_MISO_shift(R_MISO_shift'high); 77 | R_bit_counter <= R_bit_counter + 1; 78 | R_ram_we <= '0'; 79 | else 80 | if R_SCLK_shift = "01" then -- SCLK falling edge 81 | if R_bit_counter = "110" then 82 | if R_ram_write_addr = to_unsigned(C_track_len-1, R_ram_write_addr'length) then 83 | R_ram_write_addr <= (others => '0'); 84 | else 85 | R_ram_write_addr <= R_ram_write_addr + 1; 86 | end if; 87 | end if; 88 | if R_bit_counter = "000" then 89 | R_ram_di <= R_MOSI_shift; 90 | R_ram_we <= '1'; 91 | end if; 92 | else 93 | R_ram_we <= '0'; 94 | end if; 95 | end if; -- SCLK rising edge 96 | end if; 97 | end if; 98 | end process P_SPI_slave; 99 | MISO <= R_MISO_shift(R_MISO_shift'high) when CS = '1' else 'Z'; 100 | ram_we <= R_ram_we; 101 | ram_di <= R_ram_di; 102 | ram_write_addr <= R_ram_write_addr; 103 | end rtl; 104 | -------------------------------------------------------------------------------- /disk_ii_rom.vhd: -------------------------------------------------------------------------------- 1 | library ieee; 2 | use ieee.std_logic_1164.all; 3 | use ieee.numeric_std.all; 4 | 5 | entity disk_ii_rom is 6 | port ( 7 | addr : in unsigned(7 downto 0); 8 | clk : in std_logic; 9 | dout : out unsigned(7 downto 0)); 10 | end disk_ii_rom; 11 | 12 | architecture rtl of disk_ii_rom is 13 | type rom_array is array(0 to 255) of unsigned(7 downto 0); 14 | 15 | constant ROM : rom_array := ( 16 | X"a2", X"20", X"a0", X"00", X"a2", X"03", X"86", X"3c", 17 | X"8a", X"0a", X"24", X"3c", X"f0", X"10", X"05", X"3c", 18 | X"49", X"ff", X"29", X"7e", X"b0", X"08", X"4a", X"d0", 19 | X"fb", X"98", X"9d", X"56", X"03", X"c8", X"e8", X"10", 20 | X"e5", X"20", X"58", X"ff", X"ba", X"bd", X"00", X"01", 21 | X"0a", X"0a", X"0a", X"0a", X"85", X"2b", X"aa", X"bd", 22 | X"8e", X"c0", X"bd", X"8c", X"c0", X"bd", X"8a", X"c0", 23 | X"bd", X"89", X"c0", X"a0", X"50", X"bd", X"80", X"c0", 24 | X"98", X"29", X"03", X"0a", X"05", X"2b", X"aa", X"bd", 25 | X"81", X"c0", X"a9", X"56", X"20", X"a8", X"fc", X"88", 26 | X"10", X"eb", X"85", X"26", X"85", X"3d", X"85", X"41", 27 | X"a9", X"08", X"85", X"27", X"18", X"08", X"bd", X"8c", 28 | X"c0", X"10", X"fb", X"49", X"d5", X"d0", X"f7", X"bd", 29 | X"8c", X"c0", X"10", X"fb", X"c9", X"aa", X"d0", X"f3", 30 | X"ea", X"bd", X"8c", X"c0", X"10", X"fb", X"c9", X"96", 31 | X"f0", X"09", X"28", X"90", X"df", X"49", X"ad", X"f0", 32 | X"25", X"d0", X"d9", X"a0", X"03", X"85", X"40", X"bd", 33 | X"8c", X"c0", X"10", X"fb", X"2a", X"85", X"3c", X"bd", 34 | X"8c", X"c0", X"10", X"fb", X"25", X"3c", X"88", X"d0", 35 | X"ec", X"28", X"c5", X"3d", X"d0", X"be", X"a5", X"40", 36 | X"c5", X"41", X"d0", X"b8", X"b0", X"b7", X"a0", X"56", 37 | X"84", X"3c", X"bc", X"8c", X"c0", X"10", X"fb", X"59", 38 | X"d6", X"02", X"a4", X"3c", X"88", X"99", X"00", X"03", 39 | X"d0", X"ee", X"84", X"3c", X"bc", X"8c", X"c0", X"10", 40 | X"fb", X"59", X"d6", X"02", X"a4", X"3c", X"91", X"26", 41 | X"c8", X"d0", X"ef", X"bc", X"8c", X"c0", X"10", X"fb", 42 | X"59", X"d6", X"02", X"d0", X"87", X"a0", X"00", X"a2", 43 | X"56", X"ca", X"30", X"fb", X"b1", X"26", X"5e", X"00", 44 | X"03", X"2a", X"5e", X"00", X"03", X"2a", X"91", X"26", 45 | X"c8", X"d0", X"ee", X"e6", X"27", X"e6", X"3d", X"a5", 46 | X"3d", X"cd", X"00", X"08", X"a6", X"2b", X"90", X"db", 47 | X"4c", X"01", X"08", X"00", X"00", X"00", X"00", X"00"); 48 | 49 | begin 50 | 51 | process (clk) 52 | begin 53 | if rising_edge(clk) then 54 | dout <= ROM(TO_INTEGER(addr)); 55 | end if; 56 | end process; 57 | 58 | end rtl; 59 | -------------------------------------------------------------------------------- /dos33master.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emard/apple2fpga/0ad84ae2f9d9d5cc0e8db6128d630a9e16cdd6de/dos33master.nib -------------------------------------------------------------------------------- /dsk2nib.c: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | * 3 | * Apple ][ .dsk file to .nib file format converter 4 | * 5 | * Stephen A. Edwards, sedwards@cs.columbia.edu 6 | * 7 | * Adapted from the "dsk2pdb" program supplied with the PalmApple/Appalm ][ 8 | * 9 | *********************************************************************** 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | typedef unsigned char BYTE; 17 | 18 | #define VOLUME_NUMBER 254 19 | 20 | #define TRACKS 35 21 | #define SECTORS 16 22 | #define SECTOR_SIZE 256 23 | #define DOS_TRACK_BYTES (SECTORS * SECTOR_SIZE) 24 | 25 | #define RAW_TRACK_BYTES 0x1A00 26 | 27 | 28 | FILE *disk_file; 29 | BYTE dos_track[SECTORS * SECTOR_SIZE]; 30 | 31 | BYTE raw_track[RAW_TRACK_BYTES]; 32 | BYTE *target; /* Where to write in the raw_track buffer */ 33 | 34 | #define write_byte(x) (*target++ = (x)) 35 | 36 | BYTE GCR_encoding_table[64] = { 37 | 0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6, 38 | 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3, 39 | 0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC, 40 | 0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3, 41 | 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 42 | 0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC, 43 | 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 44 | 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF }; 45 | 46 | int Swap_Bit[4] = { 0, 2, 1, 3 }; /* swap lower 2 bits */ 47 | BYTE GCR_buffer[256]; 48 | BYTE GCR_buffer2[86]; 49 | 50 | /* physical sector no. to DOS 3.3 logical sector no. table */ 51 | int Logical_Sector[16] = { 52 | 0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 53 | 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF }; 54 | 55 | /* 56 | * write an FM encoded value, used in writing address fields 57 | */ 58 | void FM_encode( BYTE data ) 59 | { 60 | write_byte( (data >> 1) | 0xAA ); 61 | write_byte( data | 0xAA ); 62 | } 63 | 64 | /* 65 | * Write 0xFF sync bytes 66 | */ 67 | void write_sync( int length ) 68 | { 69 | while( length-- ) write_byte( 0xFF ); 70 | } 71 | 72 | void write_address_field( int volume, int track, int sector ) 73 | { 74 | /* 75 | * write address mark 76 | */ 77 | write_byte( 0xD5 ); 78 | write_byte( 0xAA ); 79 | write_byte( 0x96 ); 80 | 81 | /* 82 | * write Volume, Track, Sector & Check-sum 83 | */ 84 | FM_encode( volume ); 85 | FM_encode( track ); 86 | FM_encode( sector ); 87 | FM_encode( volume ^ track ^ sector ); 88 | 89 | /* 90 | * write epilogue 91 | */ 92 | write_byte( 0xDE ); 93 | write_byte( 0xAA ); 94 | write_byte( 0xEB ); 95 | } 96 | 97 | /* 98 | * 6-and-2 group encoding: the heart of the "nibblization" procedure 99 | */ 100 | void encode62( BYTE *page ) 101 | { 102 | int i, j; 103 | 104 | /* 86 * 3 = 258, so the first two byte are encoded twice */ 105 | GCR_buffer2[0] = Swap_Bit[page[1] & 0x03]; 106 | GCR_buffer2[1] = Swap_Bit[page[0] & 0x03]; 107 | 108 | /* save higher 6 bits in GCR_buffer and lower 2 bits in GCR_buffer2 */ 109 | for( i = 255, j = 2; i >= 0; i--, j = j == 85? 0: j + 1 ) { 110 | GCR_buffer2[j] = (GCR_buffer2[j] << 2) | Swap_Bit[page[i] & 0x03]; 111 | GCR_buffer[i] = page[i] >> 2; 112 | } 113 | 114 | /* clear off higher 2 bits of GCR_buffer2 set in the last call */ 115 | for( i = 0; i < 86; i++ ) 116 | GCR_buffer2[i] &= 0x3f; 117 | } 118 | 119 | void write_data_field(BYTE *page) 120 | { 121 | int i; 122 | BYTE last, checksum; 123 | 124 | encode62(page); 125 | 126 | /* write prologue */ 127 | write_byte( 0xD5 ); 128 | write_byte( 0xAA ); 129 | write_byte( 0xAD ); 130 | 131 | /* write GCR encoded data */ 132 | for ( i = 0x55, last = 0 ; i >= 0 ; --i ) { 133 | checksum = last ^ GCR_buffer2[i]; 134 | write_byte( GCR_encoding_table[checksum] ); 135 | last = GCR_buffer2[i]; 136 | } 137 | for ( i = 0 ; i < 256 ; ++i ) { 138 | checksum = last ^ GCR_buffer[i]; 139 | write_byte( GCR_encoding_table[checksum] ); 140 | last = GCR_buffer[i]; 141 | } 142 | 143 | /* write checksum and epilogue */ 144 | write_byte( GCR_encoding_table[last] ); 145 | write_byte( 0xDE ); 146 | write_byte( 0xAA ); 147 | write_byte( 0xEB ); 148 | } 149 | 150 | int main(int argc, char **argv) 151 | { 152 | char nibname[256], *p; 153 | FILE *nib_file; 154 | int track; 155 | 156 | if (argc < 2) { 157 | fprintf(stderr, "Usage: %s [NIB file]\n", argv[0]); 158 | exit(1); 159 | } 160 | 161 | if (!(disk_file = fopen(argv[1], "rb"))) { 162 | fprintf(stderr, "Unable to mount disk file \"%s\"\n", argv[1]); 163 | exit(1); 164 | } 165 | 166 | if (argc > 2) { 167 | strcpy(nibname, argv[2]); 168 | } else { 169 | /* Strip leading pathname from DSK name */ 170 | for (p = argv[1]; *p; p++) { 171 | if (*p == '/' || *p == '\\') 172 | argv[1] = p + 1; 173 | } 174 | strcpy(nibname, argv[1]); 175 | /* Strip trailing .dsk, if any, from DSK name */ 176 | p = nibname + strlen(nibname); 177 | if (p[-4] == '.' && 178 | (p[-3] == 'd' || p[-3] == 'D') && 179 | (p[-2] == 's' || p[-2] == 'S') && 180 | (p[-1] == 'k' || p[-1] == 'K')) p[-4] = 0; 181 | strcat(nibname, ".nib"); 182 | } 183 | 184 | if (!(nib_file = fopen(nibname, "wb"))) { 185 | fprintf(stderr, "Unable to write \"%s\"\n", nibname); 186 | exit(1); 187 | } 188 | 189 | /* Read, convert, and write each track */ 190 | 191 | for (track = 0 ; track < TRACKS ; ++track ) { 192 | int sector; 193 | 194 | fseek( disk_file, track * DOS_TRACK_BYTES, 0L ); 195 | if ( fread(dos_track, 1, DOS_TRACK_BYTES, disk_file) != DOS_TRACK_BYTES ) { 196 | fprintf(stderr, "Unexpected end of disk data\n"); 197 | exit(1); 198 | } 199 | 200 | target = raw_track; 201 | 202 | for ( sector = 0 ; sector < SECTORS ; sector ++ ) { 203 | write_sync( 38 ); /* Inter-sector gap */ 204 | write_address_field( VOLUME_NUMBER, track, sector ); 205 | write_sync( 8 ); 206 | write_data_field( dos_track + Logical_Sector[sector] * SECTOR_SIZE ); 207 | } 208 | 209 | /* Pad rest of buffer with sync bytes */ 210 | 211 | while (target != &raw_track[RAW_TRACK_BYTES]) 212 | write_byte( 0xff ); 213 | 214 | if ( fwrite(raw_track, 1, RAW_TRACK_BYTES, nib_file) != RAW_TRACK_BYTES) { 215 | fprintf(stderr, "Error writing .nib file\n"); 216 | exit(1); 217 | } 218 | } 219 | 220 | fclose(disk_file); 221 | fclose(nib_file); 222 | 223 | return 0; 224 | } 225 | 226 | /* Local Variables: */ 227 | /* compile-command: "cc -O -Wall -pedantic -ansi -o dsk2nib dsk2nib.c" */ 228 | /* End: */ 229 | -------------------------------------------------------------------------------- /dsk2nib.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual C++ Express 2008 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dsk2nib", "dsk2nib.vcproj", "{E041A5D9-82B4-42BC-86C5-FA362A7B8892}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {E041A5D9-82B4-42BC-86C5-FA362A7B8892}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {E041A5D9-82B4-42BC-86C5-FA362A7B8892}.Debug|Win32.Build.0 = Debug|Win32 14 | {E041A5D9-82B4-42BC-86C5-FA362A7B8892}.Release|Win32.ActiveCfg = Release|Win32 15 | {E041A5D9-82B4-42BC-86C5-FA362A7B8892}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /dsk2nib.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 26 | 29 | 32 | 35 | 38 | 41 | 52 | 55 | 58 | 61 | 68 | 71 | 74 | 77 | 80 | 83 | 86 | 89 | 90 | 98 | 101 | 104 | 107 | 110 | 113 | 124 | 127 | 130 | 133 | 142 | 145 | 148 | 151 | 154 | 157 | 160 | 163 | 164 | 165 | 166 | 167 | 168 | 173 | 176 | 177 | 178 | 183 | 184 | 189 | 190 | 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /esp32/linux_keyboard.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # AUTHOR=EMARD 4 | # LICENSE=GPL 5 | 6 | # use ps2recv.py on ESP32 7 | 8 | # Reads linux mouse input device (evdev). 9 | # Converts mouse events to ps2 serial commands. 10 | # Currently it can only move mouse pointer. 11 | # Mouse clicks are received but not supported yet. 12 | 13 | # lsinput 14 | # /dev/input/event7 15 | # name : "Logitech USB-PS/2 Optical Mouse" 16 | # chmod a+rw /dev/input/event7 17 | 18 | import evdev 19 | import serial 20 | import struct 21 | import socket 22 | 23 | # fix packet with header, escapes, trailer 24 | def escape(p): 25 | retval = struct.pack("BB", 0, 0) 26 | for char in p: 27 | if char == 0 or char == 0x5C: 28 | retval += struct.pack("B", 0x5C) 29 | retval += struct.pack("B", char) 30 | retval += struct.pack("B", 0) 31 | return retval 32 | 33 | def pointer(x,y): 34 | rgtr = (y & 0xFFF)*(2**12) + (x & 0xFFF) 35 | return struct.pack(">BBI", 0x15, 3, rgtr) 36 | 37 | def mouse_report(dx,dy,dz,btn_left,btn_middle,btn_right): 38 | return struct.pack(">BBBBBB", 0x0F, 3, (-dz) & 0xFF, (-dy) & 0xFF, dx & 0xFF, (btn_left & 1) + (btn_right & 1)*(2**1) + (btn_middle & 1)*(2**2)) 39 | 40 | def print_packet(x): 41 | for c in x: 42 | print("%02X" % (c), end=''); 43 | print("") 44 | 45 | if __name__ == '__main__': 46 | # this string will search for mouse in list of evdev inputs 47 | # Usually this should match a part of USB mouse device name 48 | keyboard_input_name = "TypeM" 49 | # ps2 network host or ip 50 | tcp_host = "192.168.48.118" 51 | tcp_port = 3252 # use UDP, not serial port 52 | 53 | # from http://www.vetra.com/scancodes.html 54 | keymap_ps2_scan2 = { 55 | 'KEY_GRAVE' : 0x0E, 56 | 'KEY_1' : 0x16, 57 | 'KEY_2' : 0x1E, 58 | 'KEY_3' : 0x26, 59 | 'KEY_4' : 0x25, 60 | 'KEY_5' : 0x2E, 61 | 'KEY_6' : 0x36, 62 | 'KEY_7' : 0x3D, 63 | 'KEY_8' : 0x3E, 64 | 'KEY_9' : 0x46, 65 | 'KEY_0' : 0x45, 66 | 'KEY_MINUS' : 0x4E, 67 | 'KEY_EQUAL' : 0x55, 68 | 'KEY_BACKSPACE' : 0x66, 69 | 'KEY_TAB' : 0x0D, 70 | 'KEY_Q' : 0x15, 71 | 'KEY_W' : 0x1D, 72 | 'KEY_E' : 0x24, 73 | 'KEY_R' : 0x2D, 74 | 'KEY_T' : 0x2C, 75 | 'KEY_Y' : 0x35, 76 | 'KEY_U' : 0x3C, 77 | 'KEY_I' : 0x43, 78 | 'KEY_O' : 0x44, 79 | 'KEY_P' : 0x4D, 80 | 'KEY_LEFTBRACE' : 0x54, 81 | 'KEY_RIGHTBRACE': 0x5B, 82 | 'KEY_CAPSLOCK' : 0x58, 83 | 'KEY_A' : 0x1C, 84 | 'KEY_S' : 0x1B, 85 | 'KEY_D' : 0x23, 86 | 'KEY_F' : 0x2B, 87 | 'KEY_G' : 0x34, 88 | 'KEY_H' : 0x33, 89 | 'KEY_J' : 0x3B, 90 | 'KEY_K' : 0x42, 91 | 'KEY_L' : 0x4B, 92 | 'KEY_SEMICOLON' : 0x4C, 93 | 'KEY_APOSTROPHE': 0x52, 94 | 'KEY_ENTER' : 0x5A, 95 | 'KEY_LEFTSHIFT' : 0x12, 96 | 'KEY_Z' : 0x1A, 97 | 'KEY_X' : 0x22, 98 | 'KEY_C' : 0x21, 99 | 'KEY_V' : 0x2A, 100 | 'KEY_B' : 0x32, 101 | 'KEY_N' : 0x31, 102 | 'KEY_M' : 0x3A, 103 | 'KEY_COMMA' : 0x41, 104 | 'KEY_DOT' : 0x49, 105 | 'KEY_SLASH' : 0x4A, 106 | 'KEY_RIGHTSHIFT': 0x59, 107 | 'KEY_LEFTCTRL' : 0x14, 108 | 'KEY_LEFTALT' : 0x11, 109 | 'KEY_SPACE' : 0x29, 110 | 'KEY_RIGHTALT' :(0x11 | 0x80), 111 | 'KEY_RIGHTCTRL' :(0x14 | 0x80), 112 | 'KEY_INSERT' :(0x70 | 0x80), 113 | 'KEY_DELETE' :(0x71 | 0x80), 114 | 'KEY_HOME' :(0x6C | 0x80), 115 | 'KEY_END' :(0x69 | 0x80), 116 | 'KEY_PAGEUP' :(0x7D | 0x80), 117 | 'KEY_PAGEDOWN' :(0x7A | 0x80), 118 | 'KEY_UP' :(0x75 | 0x80), 119 | 'KEY_DOWN' :(0x72 | 0x80), 120 | 'KEY_LEFT' :(0x6B | 0x80), 121 | 'KEY_RIGHT' :(0x74 | 0x80), 122 | 'KEY_NUMLOCK' :(0x77 | 0x80), 123 | 'KEY_KP7' : 0x6C, 124 | 'KEY_KP4' : 0x6B, 125 | 'KEY_KP1' : 0x69, 126 | 'KEY_KPSLASH' :(0x4A | 0x80), 127 | 'KEY_KP8' : 0x75, 128 | 'KEY_KP5' : 0x73, 129 | 'KEY_KP2' : 0x72, 130 | 'KEY_KP0' : 0x70, 131 | 'KEY_KPASTERISK': 0x7C, 132 | 'KEY_KP9' : 0x7D, 133 | 'KEY_KP6' : 0x74, 134 | 'KEY_KP3' : 0x7A, 135 | 'KEY_KPPLUS' : 0x79, 136 | 'KEY_KPENTER' :(0x5A | 0x80), 137 | 'KEY_ESC' : 0x76, 138 | 'KEY_F1' : 0x05, 139 | 'KEY_F2' : 0x06, 140 | 'KEY_F3' : 0x04, 141 | 'KEY_F4' : 0x0C, 142 | 'KEY_F5' : 0x03, 143 | 'KEY_F6' : 0x0B, 144 | 'KEY_F7' : 0x83, 145 | 'KEY_F8' : 0x0A, 146 | 'KEY_F9' : 0x01, 147 | 'KEY_F10' : 0x09, 148 | 'KEY_F11' : 0x78, 149 | 'KEY_F12' : 0x07, 150 | 'KEY_SCROLLLOCK': 0x7E, 151 | 'KEY_BACKSLASH' : 0x5D, 152 | } 153 | 154 | # convert keys to input events evdev.ecodes.ecodes[key] 155 | event2ps2 = { } 156 | for key in keymap_ps2_scan2: 157 | event2ps2[evdev.ecodes.ecodes[key]] = keymap_ps2_scan2[key] 158 | 159 | X = 0 160 | Y = 0 161 | Z = 0 162 | DX = 0 163 | DY = 0 164 | DZ = 0 165 | BTN_LEFT = 0 166 | BTN_RIGHT = 0 167 | BTN_MIDDLE = 0 168 | TOUCH = 0 169 | 170 | DEVICE = None 171 | 172 | DEVICES = [evdev.InputDevice(fn) for fn in evdev.list_devices()] 173 | 174 | for d in DEVICES: 175 | if keyboard_input_name in d.name: 176 | DEVICE = d 177 | print('Found %s at %s...' % (d.name, d.path)) 178 | break 179 | 180 | if DEVICE: 181 | ps2_tcp=socket.create_connection((tcp_host, tcp_port)) 182 | print("Sending keyboard events to %s:%s" % (tcp_host,tcp_port)) 183 | ps2_tcp.sendall(bytearray([0xAA, 0xFA])) # keyboard sends 0xAA after being plugged 184 | for event in DEVICE.read_loop(): 185 | if event.type == evdev.ecodes.EV_REL and False: # TODO support mouse properly 186 | if event.code == evdev.ecodes.REL_X: 187 | DX = event.value 188 | X += DX 189 | if event.code == evdev.ecodes.REL_Y: 190 | DY = event.value 191 | Y += DY 192 | if event.code == evdev.ecodes.REL_WHEEL: 193 | DZ = event.value 194 | Z += DZ 195 | #print('X=%d Y=%d Z=%d' % (X, Y, Z)) 196 | #packet = pointer(X,Y) 197 | packet = mouse_report(DX,DY,DZ,BTN_LEFT,BTN_MIDDLE,BTN_RIGHT) 198 | DZ = 0 199 | DX = 0 200 | DY = 0 201 | ps2_tcp.sendall(packet) 202 | 203 | if event.type == evdev.ecodes.EV_KEY: 204 | if event.code in event2ps2: 205 | packet = None 206 | code = event2ps2[event.code] 207 | if event.value == 1: # key press 208 | if code & 0x80: 209 | packet = bytearray([0xE0, code & 0x7F]) 210 | else: 211 | packet = bytearray([code & 0x7F]) 212 | #if event.value == 2: # key autorepeat 213 | # packet = bytearray([event2ps2[event.code]]) 214 | if event.value == 0: # key release 215 | if code & 0x80: 216 | packet = bytearray([0xE0, 0xF0, code & 0x7F]) 217 | else: 218 | packet = bytearray([0xF0, code & 0x7F]) 219 | if packet: 220 | ps2_tcp.sendall(packet) 221 | -------------------------------------------------------------------------------- /esp32/main.py: -------------------------------------------------------------------------------- 1 | import network 2 | sta_if = network.WLAN(network.STA_IF) 3 | sta_if.active(True) 4 | sta_if.connect("accesspoint", "password") 5 | #from os import mount 6 | #from machine import SDCard 7 | #mount(SDCard(slot=3),"/sd") 8 | import ecp5 9 | ecp5.prog("apple2.bit.gz") 10 | import disk2 11 | import ps2server 12 | -------------------------------------------------------------------------------- /esp32/ps2.py: -------------------------------------------------------------------------------- 1 | # micropython ESP32 2 | # PS/2 protocol emulator (keyboard and mouse, transmit-only) 3 | 4 | # AUTHOR=EMARD 5 | # LICENSE=BSD 6 | 7 | from time import sleep_us 8 | from machine import Pin 9 | from micropython import const 10 | from uctypes import addressof 11 | 12 | class ps2: 13 | def __init__(self, kbd_clk=26, kbd_data=25, mouse_clk=17, mouse_data=16, qbit_us=16, byte_us=150, f0delay_us=0): 14 | self.gpio_kbd_clk = kbd_clk 15 | self.gpio_kbd_data = kbd_data 16 | self.gpio_mouse_clk = mouse_clk 17 | self.gpio_mouse_data = mouse_data 18 | self.keyboard() 19 | self.qbit_us = qbit_us # quarter-bit delay 20 | self.byte_us = byte_us # byte-to-byte delay 21 | self.f0delay_us = f0delay_us # additional delay before 0xF0 and a after a byte after 0xF0 22 | 23 | def keyboard(self): 24 | self.ps2_clk = Pin(self.gpio_kbd_clk, Pin.OPEN_DRAIN, Pin.PULL_UP) 25 | self.ps2_data = Pin(self.gpio_kbd_data, Pin.OPEN_DRAIN, Pin.PULL_UP) 26 | self.ps2_clk.on() 27 | self.ps2_data.on() 28 | 29 | def mouse(self): 30 | self.ps2_clk = Pin(self.gpio_mouse_clk, Pin.OPEN_DRAIN, Pin.PULL_UP) 31 | self.ps2_data = Pin(self.gpio_mouse_data, Pin.OPEN_DRAIN, Pin.PULL_UP) 32 | self.ps2_clk.on() 33 | self.ps2_data.on() 34 | 35 | @micropython.viper 36 | def write(self, data): 37 | qbit_us = int(self.qbit_us) 38 | f0delay = int(self.f0delay_us) 39 | f0c = 0 40 | p = ptr8(addressof(data)) 41 | l = int(len(data)) 42 | for i in range(l): 43 | val = p[i] 44 | if val == 0xF0 and f0delay > 0: 45 | sleep_us(f0delay) 46 | f0c = 2 47 | parity = 1 48 | self.ps2_data.off() 49 | sleep_us(qbit_us) 50 | self.ps2_clk.off() 51 | sleep_us(qbit_us+qbit_us) 52 | self.ps2_clk.on() 53 | sleep_us(qbit_us) 54 | for nf in range(8): 55 | if val & 1: 56 | self.ps2_data.on() 57 | parity ^= 1 58 | else: 59 | self.ps2_data.off() 60 | parity ^= 0 # keep timing the same as above 61 | sleep_us(qbit_us) 62 | self.ps2_clk.off() 63 | val >>= 1 64 | sleep_us(qbit_us+qbit_us) 65 | self.ps2_clk.on() 66 | sleep_us(qbit_us) 67 | if parity: 68 | self.ps2_data.on() 69 | else: 70 | self.ps2_data.off() 71 | sleep_us(qbit_us) 72 | self.ps2_clk.off() 73 | sleep_us(qbit_us+qbit_us) 74 | self.ps2_clk.on() 75 | sleep_us(qbit_us) 76 | self.ps2_data.on() 77 | sleep_us(qbit_us) 78 | self.ps2_clk.off() 79 | sleep_us(qbit_us+qbit_us) 80 | self.ps2_clk.on() 81 | sleep_us(self.byte_us) 82 | if f0c > 0: 83 | f0c -= 1 84 | if f0c == 0: 85 | sleep_us(f0delay) 86 | -------------------------------------------------------------------------------- /esp32/ps2recv.py: -------------------------------------------------------------------------------- 1 | # AUTHOR=EMARD 2 | # LICENSE=BSD 3 | 4 | # PS/2 receiver converts from TCP to PS/2 5 | # use linux_keyboard.py or pygame_mouse.py on host side 6 | # edit ps2port (see below) 7 | 8 | import socket 9 | import network 10 | import uos 11 | import gc 12 | from time import sleep_us 13 | from struct import unpack 14 | from micropython import alloc_emergency_exception_buf 15 | from micropython import const 16 | from uctypes import addressof 17 | import ps2 18 | 19 | ps2port=ps2.ps2( 20 | # v3.0.x 21 | kbd_clk = 26, # gp[11] 22 | kbd_data = 25, # gn[11] 23 | mouse_clk = 26, # wifi_gpio17 24 | mouse_data = 25, # wifi_gpio16 25 | # v3.1.4 26 | #kbd_clk = 22, # wifi_gpio22 27 | #kbd_data = 21, # wifi_gpio21 28 | #mouse_clk = 22, # wifi_gpio27 29 | #mouse_data = 21, # wifi_gpio26 30 | qbit_us=16, 31 | byte_us=150 32 | ) 33 | 34 | # constant definitions 35 | _SO_REGISTER_HANDLER = const(20) 36 | _COMMAND_TIMEOUT = const(300) 37 | 38 | # Global variables 39 | ps2socket = None 40 | client_list = [] 41 | verbose_l = 0 42 | client_busy = False 43 | 44 | mouse = 0 # global tracker: 0 keyboard, 1 mouse 45 | 46 | class PS2_client: 47 | 48 | def __init__(self, ps2socket): 49 | self.command_client, self.remote_addr = ps2socket.accept() 50 | self.remote_addr = self.remote_addr[0] 51 | self.command_client.settimeout(_COMMAND_TIMEOUT) 52 | log_msg(1, "PS2 Command connection from:", self.remote_addr) 53 | self.command_client.setsockopt(socket.SOL_SOCKET, 54 | _SO_REGISTER_HANDLER, 55 | self.exec_ps2_command) 56 | # simple PS/2 packet parser state 57 | self.state = 0 58 | self.length = 0 59 | self.index = 0 60 | self.wait = 0 # 0 sending, 1 waiting 61 | self.packet = bytearray(256) 62 | 63 | def packet_parser(self, data): 64 | global mouse 65 | for val in data: 66 | if self.state == 0: # K/M/W 67 | if val == 75: # K 68 | if mouse != 0: 69 | mouse = 0 70 | ps2port.keyboard() 71 | self.state = 1 72 | #if val == 77: # M 73 | # if mouse != 1: 74 | # mouse = 1 75 | # ps2port.mouse() 76 | # self.state = 1 77 | if val == 87: # W 78 | self.wait = 1 79 | self.state = 1 80 | continue 81 | if self.state == 1: # length 82 | self.length = val 83 | self.index = 0 84 | self.state = 2 85 | continue 86 | if self.state == 2: # packet data 87 | self.packet[self.index] = val 88 | self.index += 1 89 | if self.index >= self.length: 90 | if self.wait: 91 | sleep_us(unpack("= level: 127 | print(*args) 128 | 129 | 130 | # close client and remove it from the list 131 | def close_client(cl): 132 | cl.setsockopt(socket.SOL_SOCKET, _SO_REGISTER_HANDLER, None) 133 | cl.close() 134 | for i, client in enumerate(client_list): 135 | if client.command_client == cl: 136 | del client_list[i] 137 | break 138 | 139 | 140 | def accept_ps2_connect(ps2socket): 141 | # Accept new calls for the server 142 | try: 143 | client_list.append(PS2_client(ps2socket)) 144 | except: 145 | log_msg(1, "Attempt to connect failed") 146 | # try at least to reject 147 | try: 148 | temp_client, temp_addr = ps2socket.accept() 149 | temp_client.close() 150 | except: 151 | pass 152 | 153 | 154 | def stop(): 155 | global ps2socket 156 | global client_list 157 | global client_busy 158 | global ps2port 159 | 160 | for client in client_list: 161 | client.command_client.setsockopt(socket.SOL_SOCKET, 162 | _SO_REGISTER_HANDLER, None) 163 | client.command_client.close() 164 | del client_list 165 | client_list = [] 166 | client_busy = False 167 | if ps2socket is not None: 168 | ps2socket.setsockopt(socket.SOL_SOCKET, _SO_REGISTER_HANDLER, None) 169 | ps2socket.close() 170 | del ps2port 171 | 172 | 173 | # start listening for ftp connections on port 21 174 | def start(port=3252, verbose=0, splash=True): 175 | global ps2socket 176 | global verbose_l 177 | global client_list 178 | global client_busy 179 | global ps2port 180 | 181 | alloc_emergency_exception_buf(100) 182 | verbose_l = verbose 183 | client_list = [] 184 | client_busy = False 185 | 186 | ps2socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 187 | ps2socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 188 | ps2socket.bind(('0.0.0.0', port)) 189 | ps2socket.listen(0) 190 | ps2socket.setsockopt(socket.SOL_SOCKET, 191 | _SO_REGISTER_HANDLER, accept_ps2_connect) 192 | 193 | 194 | def restart(port=3252, verbose=0, splash=True): 195 | stop() 196 | sleep_us(200000) 197 | start(port, verbose, splash) 198 | 199 | 200 | start(splash=True) 201 | -------------------------------------------------------------------------------- /esp32/pygame_keyboard_mouse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # AUTHOR=EMARD 4 | # LICENSE=GPL 5 | 6 | # use ps2recv.py on ESP32 (set pinout at ps2recv.py) 7 | # edit "mouse_wheel" (below) 8 | # False: send 3-byte reports as no-wheel mouse (legacy/uninitialized PS/2) 9 | # True: send 4-byte reports as wheel mouse (modern PS/2) 10 | 11 | 12 | import pygame 13 | import struct 14 | import socket 15 | 16 | tcp_host = "192.168.48.132" 17 | tcp_port = 3252 18 | mouse_wheel = False 19 | 20 | ps2_tcp=socket.create_connection((tcp_host, tcp_port)) 21 | print("Sending keyboard/mouse events to %s:%s" % (tcp_host,tcp_port)) 22 | #ps2_tcp.sendall(bytearray([0xAA, 0x00, 0xFA])) 23 | # mouse sends 0xAA 0x00 after being plugged 24 | # 0xFA is ACK what mouse sends after being configured 25 | 26 | def mouse_wheel_report(dx,dy,dz,btn_left,btn_middle,btn_right): 27 | return struct.pack("> 8) & 1)<<4) + 34 | (((((-dy) & 0x100) >> 8) & 1)<<5), 35 | dx & 0xFF, 36 | (-dy) & 0xFF, 37 | (-dz) & 0x0F, 38 | ord('W'), 2, # 2-byte wait value (us, LSB first) 39 | 1000 # us wait 40 | ) 41 | 42 | def mouse_nowheel_report(dx,dy,btn_left,btn_middle,btn_right): 43 | return struct.pack("> 8) & 1)<<4) + 48 | (((((-dy) & 0x100) >> 8) & 1)<<5), 49 | dx & 0xFF, 50 | (-dy) & 0xFF, 51 | ord('W'), 2, # 2-byte wait value (us, LSB first) 52 | 1000 # us wait 53 | ) 54 | 55 | 56 | pygame.init() 57 | (width, height) = (320, 200) 58 | screen = pygame.display.set_mode((width, height)) 59 | pygame.display.set_caption(u'Press PAUSE to quit') 60 | pygame.display.flip() 61 | pygame.event.set_grab(True) 62 | pygame.mouse.set_visible(False) 63 | 64 | event2ps2 = { 65 | pygame.K_1 : 0x16, 66 | pygame.K_2 : 0x1E, 67 | pygame.K_3 : 0x26, 68 | pygame.K_4 : 0x25, 69 | pygame.K_5 : 0x2E, 70 | pygame.K_6 : 0x36, 71 | pygame.K_7 : 0x3D, 72 | pygame.K_8 : 0x3E, 73 | pygame.K_9 : 0x46, 74 | pygame.K_0 : 0x45, 75 | pygame.K_MINUS : 0x4E, 76 | pygame.K_EQUALS : 0x55, 77 | pygame.K_BACKSPACE : 0x66, 78 | pygame.K_TAB : 0x0D, 79 | pygame.K_q : 0x15, 80 | pygame.K_w : 0x1D, 81 | pygame.K_e : 0x24, 82 | pygame.K_r : 0x2D, 83 | pygame.K_t : 0x2C, 84 | pygame.K_y : 0x35, 85 | pygame.K_u : 0x3C, 86 | pygame.K_i : 0x43, 87 | pygame.K_o : 0x44, 88 | pygame.K_p : 0x4D, 89 | pygame.K_LEFTBRACKET : 0x54, 90 | pygame.K_RIGHTBRACKET : 0x5B, 91 | pygame.K_CAPSLOCK : 0x58, 92 | pygame.K_a : 0x1C, 93 | pygame.K_s : 0x1B, 94 | pygame.K_d : 0x23, 95 | pygame.K_f : 0x2B, 96 | pygame.K_g : 0x34, 97 | pygame.K_h : 0x33, 98 | pygame.K_j : 0x3B, 99 | pygame.K_k : 0x42, 100 | pygame.K_l : 0x4B, 101 | pygame.K_SEMICOLON : 0x4C, 102 | pygame.K_QUOTE : 0x52, 103 | pygame.K_RETURN : 0x5A, 104 | pygame.K_LSHIFT : 0x12, 105 | pygame.K_z : 0x1A, 106 | pygame.K_x : 0x22, 107 | pygame.K_c : 0x21, 108 | pygame.K_v : 0x2A, 109 | pygame.K_b : 0x32, 110 | pygame.K_n : 0x31, 111 | pygame.K_m : 0x3A, 112 | pygame.K_COMMA : 0x41, 113 | pygame.K_PERIOD : 0x49, 114 | pygame.K_SLASH : 0x4A, 115 | pygame.K_RSHIFT : 0x59, 116 | pygame.K_LCTRL : 0x14, 117 | pygame.K_LALT : 0x11, 118 | pygame.K_SPACE : 0x29, 119 | pygame.K_RALT :(0x11 | 0x80), 120 | pygame.K_RCTRL :(0x14 | 0x80), 121 | pygame.K_INSERT :(0x70 | 0x80), 122 | pygame.K_DELETE :(0x71 | 0x80), 123 | pygame.K_HOME :(0x6C | 0x80), 124 | pygame.K_END :(0x69 | 0x80), 125 | pygame.K_PAGEUP :(0x7D | 0x80), 126 | pygame.K_PAGEDOWN :(0x7A | 0x80), 127 | pygame.K_UP :(0x75 | 0x80), 128 | pygame.K_DOWN :(0x72 | 0x80), 129 | pygame.K_LEFT :(0x6B | 0x80), 130 | pygame.K_RIGHT :(0x74 | 0x80), 131 | pygame.K_NUMLOCK :(0x77 | 0x80), 132 | pygame.K_KP7 : 0x6C, 133 | pygame.K_KP4 : 0x6B, 134 | pygame.K_KP1 : 0x69, 135 | pygame.K_KP_DIVIDE :(0x4A | 0x80), 136 | pygame.K_KP8 : 0x75, 137 | pygame.K_KP5 : 0x73, 138 | pygame.K_KP2 : 0x72, 139 | pygame.K_KP0 : 0x70, 140 | pygame.K_KP_MULTIPLY : 0x7C, 141 | pygame.K_KP9 : 0x7D, 142 | pygame.K_KP6 : 0x74, 143 | pygame.K_KP3 : 0x7A, 144 | pygame.K_KP_PLUS : 0x79, 145 | pygame.K_KP_ENTER :(0x5A | 0x80), 146 | pygame.K_ESCAPE : 0x76, 147 | pygame.K_F1 : 0x05, 148 | pygame.K_F2 : 0x06, 149 | pygame.K_F3 : 0x04, 150 | pygame.K_F4 : 0x0C, 151 | pygame.K_F5 : 0x03, 152 | pygame.K_F6 : 0x0B, 153 | pygame.K_F7 : 0x83, 154 | pygame.K_F8 : 0x0A, 155 | pygame.K_F9 : 0x01, 156 | pygame.K_F10 : 0x09, 157 | pygame.K_F11 : 0x78, 158 | pygame.K_F12 : 0x07, 159 | pygame.K_SCROLLOCK : 0x7E, 160 | pygame.K_BACKSLASH : 0x5D, 161 | } 162 | 163 | while(True): 164 | event = pygame.event.wait() 165 | if event.type == pygame.KEYDOWN: 166 | if event.key == pygame.K_PAUSE: 167 | print("QUIT") 168 | break 169 | if event.key in event2ps2: 170 | code = event2ps2[event.key] 171 | if code & 0x80: 172 | packet = bytearray([ord('K'), 2, 0xE0, code & 0x7F]) 173 | else: 174 | packet = bytearray([ord('K'), 1, code & 0x7F]) 175 | ps2_tcp.sendall(packet) 176 | continue 177 | if event.type == pygame.KEYUP: 178 | if event.key in event2ps2: 179 | code = event2ps2[event.key] 180 | if code & 0x80: 181 | packet = bytearray([ord('K'), 3, 0xE0, 0xF0, code & 0x7F]) 182 | else: 183 | packet = bytearray([ord('K'), 2, 0xF0, code & 0x7F]) 184 | ps2_tcp.sendall(packet) 185 | continue 186 | wheel = 0 187 | if event.type == pygame.MOUSEBUTTONDOWN: # for wheel events 188 | if event.button == 4: # wheel UP 189 | wheel = -1 190 | if event.button == 5: # wheel DOWN 191 | wheel = 1 192 | (dx, dy) = pygame.mouse.get_rel() 193 | dz = wheel 194 | (btn_left, btn_middle, btn_right) = pygame.mouse.get_pressed() 195 | 196 | if mouse_wheel: 197 | # mouse with wheel 198 | report = mouse_wheel_report(dx, dy, dz, btn_left, btn_middle, btn_right) 199 | #print("0x%08X: X=%4d, Y=%4d, Z=%2d, L=%2d, M=%2d, R=%2d" % (struct.unpack("I",report)[0], dx, dy, dz, btn_left, btn_middle, btn_right)) 200 | else: 201 | # mouse without wheel 202 | report = mouse_nowheel_report(dx, dy, btn_left, btn_middle, btn_right) 203 | #print("X=%4d, Y=%4d, L=%2d, M=%2d, R=%2d" % (dx, dy, btn_left, btn_middle, btn_right)) 204 | #ps2_tcp.sendall(bytearray(report)) 205 | -------------------------------------------------------------------------------- /esp32/v31x/ps2.py: -------------------------------------------------------------------------------- 1 | # micropython ESP32 2 | # PS/2 protocol emulator (keyboard and mouse, transmit-only) 3 | 4 | # AUTHOR=EMARD 5 | # LICENSE=BSD 6 | 7 | from time import sleep_us 8 | from machine import Pin 9 | from micropython import const 10 | from uctypes import addressof 11 | 12 | class ps2: 13 | def __init__(self, kbd_clk=26, kbd_data=25, mouse_clk=17, mouse_data=16, qbit_us=16, byte_us=150): 14 | self.gpio_kbd_clk = kbd_clk 15 | self.gpio_kbd_data = kbd_data 16 | self.gpio_mouse_clk = mouse_clk 17 | self.gpio_mouse_data = mouse_data 18 | self.keyboard() 19 | self.qbit_us = qbit_us # quarter-bit delay 20 | self.byte_us = byte_us # byte-to-byte delay 21 | 22 | def keyboard(self): 23 | self.ps2_clk = Pin(self.gpio_kbd_clk, Pin.OPEN_DRAIN, Pin.PULL_UP) 24 | self.ps2_data = Pin(self.gpio_kbd_data, Pin.OPEN_DRAIN, Pin.PULL_UP) 25 | self.ps2_clk.on() 26 | self.ps2_data.on() 27 | 28 | def mouse(self): 29 | self.ps2_clk = Pin(self.gpio_mouse_clk, Pin.OPEN_DRAIN, Pin.PULL_UP) 30 | self.ps2_data = Pin(self.gpio_mouse_data, Pin.OPEN_DRAIN, Pin.PULL_UP) 31 | self.ps2_clk.on() 32 | self.ps2_data.on() 33 | 34 | @micropython.viper 35 | def write(self, data): 36 | qbit_us = int(self.qbit_us) 37 | p = ptr8(addressof(data)) 38 | l = int(len(data)) 39 | for i in range(l): 40 | val = p[i] 41 | parity = 1 42 | self.ps2_data.off() 43 | sleep_us(qbit_us) 44 | self.ps2_clk.off() 45 | sleep_us(qbit_us+qbit_us) 46 | self.ps2_clk.on() 47 | sleep_us(qbit_us) 48 | for nf in range(8): 49 | if val & 1: 50 | self.ps2_data.on() 51 | parity ^= 1 52 | else: 53 | self.ps2_data.off() 54 | parity ^= 0 # keep timing the same as above 55 | sleep_us(qbit_us) 56 | self.ps2_clk.off() 57 | val >>= 1 58 | sleep_us(qbit_us+qbit_us) 59 | self.ps2_clk.on() 60 | sleep_us(qbit_us) 61 | if parity: 62 | self.ps2_data.on() 63 | else: 64 | self.ps2_data.off() 65 | sleep_us(qbit_us) 66 | self.ps2_clk.off() 67 | sleep_us(qbit_us+qbit_us) 68 | self.ps2_clk.on() 69 | sleep_us(qbit_us) 70 | self.ps2_data.on() 71 | sleep_us(qbit_us) 72 | self.ps2_clk.off() 73 | sleep_us(qbit_us+qbit_us) 74 | self.ps2_clk.on() 75 | sleep_us(self.byte_us) 76 | -------------------------------------------------------------------------------- /esp32/v31x/ps2recv.py: -------------------------------------------------------------------------------- 1 | # AUTHOR=EMARD 2 | # LICENSE=BSD 3 | 4 | # PS/2 receiver converts from TCP to PS/2 5 | # use linux_keyboard.py or pygame_mouse.py on host side 6 | # edit ps2port (see below) 7 | 8 | import socket 9 | import network 10 | import uos 11 | import gc 12 | from time import sleep_us 13 | from struct import unpack 14 | from micropython import alloc_emergency_exception_buf 15 | from micropython import const 16 | from uctypes import addressof 17 | import ps2 18 | 19 | ps2port=ps2.ps2( 20 | # v3.0.x 21 | #kbd_clk = 26, # gp[11] 22 | #kbd_data = 25, # gn[11] 23 | #mouse_clk = 17, # wifi_gpio17 24 | #mouse_data = 16, # wifi_gpio16 25 | # v3.1.4 26 | kbd_clk = 22, # wifi_gpio22 27 | kbd_data = 21, # wifi_gpio21 28 | mouse_clk = 22, # wifi_gpio27 29 | mouse_data = 21, # wifi_gpio26 30 | qbit_us=16, 31 | byte_us=150 32 | ) 33 | 34 | # constant definitions 35 | _SO_REGISTER_HANDLER = const(20) 36 | _COMMAND_TIMEOUT = const(300) 37 | 38 | # Global variables 39 | ps2socket = None 40 | client_list = [] 41 | verbose_l = 0 42 | client_busy = False 43 | 44 | mouse = 0 # global tracker: 0 keyboard, 1 mouse 45 | 46 | class PS2_client: 47 | 48 | def __init__(self, ps2socket): 49 | self.command_client, self.remote_addr = ps2socket.accept() 50 | self.remote_addr = self.remote_addr[0] 51 | self.command_client.settimeout(_COMMAND_TIMEOUT) 52 | log_msg(1, "PS2 Command connection from:", self.remote_addr) 53 | self.command_client.setsockopt(socket.SOL_SOCKET, 54 | _SO_REGISTER_HANDLER, 55 | self.exec_ps2_command) 56 | # simple PS/2 packet parser state 57 | self.state = 0 58 | self.length = 0 59 | self.index = 0 60 | self.wait = 0 # 0 sending, 1 waiting 61 | self.packet = bytearray(256) 62 | 63 | def packet_parser(self, data): 64 | global mouse 65 | for val in data: 66 | if self.state == 0: # K/M/W 67 | if val == 75: # K 68 | if mouse != 0: 69 | mouse = 0 70 | ps2port.keyboard() 71 | self.state = 1 72 | #if val == 77: # M 73 | # if mouse != 1: 74 | # mouse = 1 75 | # ps2port.mouse() 76 | # self.state = 1 77 | if val == 87: # W 78 | self.wait = 1 79 | self.state = 1 80 | continue 81 | if self.state == 1: # length 82 | self.length = val 83 | self.index = 0 84 | self.state = 2 85 | continue 86 | if self.state == 2: # packet data 87 | self.packet[self.index] = val 88 | self.index += 1 89 | if self.index >= self.length: 90 | if self.wait: 91 | sleep_us(unpack("= level: 127 | print(*args) 128 | 129 | 130 | # close client and remove it from the list 131 | def close_client(cl): 132 | cl.setsockopt(socket.SOL_SOCKET, _SO_REGISTER_HANDLER, None) 133 | cl.close() 134 | for i, client in enumerate(client_list): 135 | if client.command_client == cl: 136 | del client_list[i] 137 | break 138 | 139 | 140 | def accept_ps2_connect(ps2socket): 141 | # Accept new calls for the server 142 | try: 143 | client_list.append(PS2_client(ps2socket)) 144 | except: 145 | log_msg(1, "Attempt to connect failed") 146 | # try at least to reject 147 | try: 148 | temp_client, temp_addr = ps2socket.accept() 149 | temp_client.close() 150 | except: 151 | pass 152 | 153 | 154 | def stop(): 155 | global ps2socket 156 | global client_list 157 | global client_busy 158 | global ps2port 159 | 160 | for client in client_list: 161 | client.command_client.setsockopt(socket.SOL_SOCKET, 162 | _SO_REGISTER_HANDLER, None) 163 | client.command_client.close() 164 | del client_list 165 | client_list = [] 166 | client_busy = False 167 | if ps2socket is not None: 168 | ps2socket.setsockopt(socket.SOL_SOCKET, _SO_REGISTER_HANDLER, None) 169 | ps2socket.close() 170 | del ps2port 171 | 172 | 173 | # start listening for ftp connections on port 21 174 | def start(port=3252, verbose=0, splash=True): 175 | global ps2socket 176 | global verbose_l 177 | global client_list 178 | global client_busy 179 | global ps2port 180 | 181 | 182 | alloc_emergency_exception_buf(100) 183 | verbose_l = verbose 184 | client_list = [] 185 | client_busy = False 186 | 187 | ps2socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 188 | ps2socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 189 | ps2socket.bind(('0.0.0.0', port)) 190 | ps2socket.listen(0) 191 | ps2socket.setsockopt(socket.SOL_SOCKET, 192 | _SO_REGISTER_HANDLER, accept_ps2_connect) 193 | 194 | 195 | def restart(port=3252, verbose=0, splash=True): 196 | stop() 197 | sleep_us(200000) 198 | start(port, verbose, splash) 199 | 200 | 201 | start(splash=True) 202 | -------------------------------------------------------------------------------- /esp32/v31x/pygame_keyboard_mouse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # AUTHOR=EMARD 4 | # LICENSE=GPL 5 | 6 | # use ps2recv.py on ESP32 (set pinout at ps2recv.py) 7 | # edit "mouse_wheel" (below) 8 | # False: send 3-byte reports as no-wheel mouse (legacy/uninitialized PS/2) 9 | # True: send 4-byte reports as wheel mouse (modern PS/2) 10 | 11 | 12 | import pygame 13 | import struct 14 | import socket 15 | 16 | tcp_host = "192.168.48.174" 17 | tcp_port = 3252 18 | mouse_wheel = False 19 | 20 | ps2_tcp=socket.create_connection((tcp_host, tcp_port)) 21 | print("Sending mouse events to %s:%s" % (tcp_host,tcp_port)) 22 | #ps2_tcp.sendall(bytearray([0xAA, 0x00, 0xFA])) 23 | # mouse sends 0xAA 0x00 after being plugged 24 | # 0xFA is ACK what mouse sends after being configured 25 | 26 | def mouse_wheel_report(dx,dy,dz,btn_left,btn_middle,btn_right): 27 | return struct.pack("> 8) & 1)<<4) + 34 | (((((-dy) & 0x100) >> 8) & 1)<<5), 35 | dx & 0xFF, 36 | (-dy) & 0xFF, 37 | (-dz) & 0x0F, 38 | ord('W'), 2, # 2-byte wait value (us, LSB first) 39 | 1000 # us wait 40 | ) 41 | 42 | def mouse_nowheel_report(dx,dy,btn_left,btn_middle,btn_right): 43 | return struct.pack("> 8) & 1)<<4) + 48 | (((((-dy) & 0x100) >> 8) & 1)<<5), 49 | dx & 0xFF, 50 | (-dy) & 0xFF, 51 | ord('W'), 2, # 2-byte wait value (us, LSB first) 52 | 1000 # us wait 53 | ) 54 | 55 | 56 | pygame.init() 57 | (width, height) = (320, 200) 58 | screen = pygame.display.set_mode((width, height)) 59 | pygame.display.set_caption(u'Press PAUSE to quit') 60 | pygame.display.flip() 61 | pygame.event.set_grab(True) 62 | pygame.mouse.set_visible(False) 63 | 64 | event2ps2 = { 65 | pygame.K_1 : 0x16, 66 | pygame.K_2 : 0x1E, 67 | pygame.K_3 : 0x26, 68 | pygame.K_4 : 0x25, 69 | pygame.K_5 : 0x2E, 70 | pygame.K_6 : 0x36, 71 | pygame.K_7 : 0x3D, 72 | pygame.K_8 : 0x3E, 73 | pygame.K_9 : 0x46, 74 | pygame.K_0 : 0x45, 75 | pygame.K_MINUS : 0x4E, 76 | pygame.K_EQUALS : 0x55, 77 | pygame.K_BACKSPACE : 0x66, 78 | pygame.K_TAB : 0x0D, 79 | pygame.K_q : 0x15, 80 | pygame.K_w : 0x1D, 81 | pygame.K_e : 0x24, 82 | pygame.K_r : 0x2D, 83 | pygame.K_t : 0x2C, 84 | pygame.K_y : 0x35, 85 | pygame.K_u : 0x3C, 86 | pygame.K_i : 0x43, 87 | pygame.K_o : 0x44, 88 | pygame.K_p : 0x4D, 89 | pygame.K_LEFTBRACKET : 0x54, 90 | pygame.K_RIGHTBRACKET : 0x5B, 91 | pygame.K_CAPSLOCK : 0x58, 92 | pygame.K_a : 0x1C, 93 | pygame.K_s : 0x1B, 94 | pygame.K_d : 0x23, 95 | pygame.K_f : 0x2B, 96 | pygame.K_g : 0x34, 97 | pygame.K_h : 0x33, 98 | pygame.K_j : 0x3B, 99 | pygame.K_k : 0x42, 100 | pygame.K_l : 0x4B, 101 | pygame.K_SEMICOLON : 0x4C, 102 | pygame.K_QUOTE : 0x52, 103 | pygame.K_RETURN : 0x5A, 104 | pygame.K_LSHIFT : 0x12, 105 | pygame.K_z : 0x1A, 106 | pygame.K_x : 0x22, 107 | pygame.K_c : 0x21, 108 | pygame.K_v : 0x2A, 109 | pygame.K_b : 0x32, 110 | pygame.K_n : 0x31, 111 | pygame.K_m : 0x3A, 112 | pygame.K_COMMA : 0x41, 113 | pygame.K_PERIOD : 0x49, 114 | pygame.K_SLASH : 0x4A, 115 | pygame.K_RSHIFT : 0x59, 116 | pygame.K_LCTRL : 0x14, 117 | pygame.K_LALT : 0x11, 118 | pygame.K_SPACE : 0x29, 119 | pygame.K_RALT :(0x11 | 0x80), 120 | pygame.K_RCTRL :(0x14 | 0x80), 121 | pygame.K_INSERT :(0x70 | 0x80), 122 | pygame.K_DELETE :(0x71 | 0x80), 123 | pygame.K_HOME :(0x6C | 0x80), 124 | pygame.K_END :(0x69 | 0x80), 125 | pygame.K_PAGEUP :(0x7D | 0x80), 126 | pygame.K_PAGEDOWN :(0x7A | 0x80), 127 | pygame.K_UP :(0x75 | 0x80), 128 | pygame.K_DOWN :(0x72 | 0x80), 129 | pygame.K_LEFT :(0x6B | 0x80), 130 | pygame.K_RIGHT :(0x74 | 0x80), 131 | pygame.K_NUMLOCK :(0x77 | 0x80), 132 | pygame.K_KP7 : 0x6C, 133 | pygame.K_KP4 : 0x6B, 134 | pygame.K_KP1 : 0x69, 135 | pygame.K_KP_DIVIDE :(0x4A | 0x80), 136 | pygame.K_KP8 : 0x75, 137 | pygame.K_KP5 : 0x73, 138 | pygame.K_KP2 : 0x72, 139 | pygame.K_KP0 : 0x70, 140 | pygame.K_KP_MULTIPLY : 0x7C, 141 | pygame.K_KP9 : 0x7D, 142 | pygame.K_KP6 : 0x74, 143 | pygame.K_KP3 : 0x7A, 144 | pygame.K_KP_PLUS : 0x79, 145 | pygame.K_KP_ENTER :(0x5A | 0x80), 146 | pygame.K_ESCAPE : 0x76, 147 | pygame.K_F1 : 0x05, 148 | pygame.K_F2 : 0x06, 149 | pygame.K_F3 : 0x04, 150 | pygame.K_F4 : 0x0C, 151 | pygame.K_F5 : 0x03, 152 | pygame.K_F6 : 0x0B, 153 | pygame.K_F7 : 0x83, 154 | pygame.K_F8 : 0x0A, 155 | pygame.K_F9 : 0x01, 156 | pygame.K_F10 : 0x09, 157 | pygame.K_F11 : 0x78, 158 | pygame.K_F12 : 0x07, 159 | pygame.K_SCROLLOCK : 0x7E, 160 | pygame.K_BACKSLASH : 0x5D, 161 | } 162 | 163 | while(True): 164 | event = pygame.event.wait() 165 | if event.type == pygame.KEYDOWN: 166 | if event.key == pygame.K_PAUSE: 167 | print("QUIT") 168 | break 169 | if event.key in event2ps2: 170 | code = event2ps2[event.key] 171 | if code & 0x80: 172 | packet = bytearray([ord('K'), 2, 0xE0, code & 0x7F]) 173 | else: 174 | packet = bytearray([ord('K'), 1, code & 0x7F]) 175 | ps2_tcp.sendall(packet) 176 | continue 177 | if event.type == pygame.KEYUP: 178 | if event.key in event2ps2: 179 | code = event2ps2[event.key] 180 | if code & 0x80: 181 | packet = bytearray([ord('K'), 3, 0xE0, 0xF0, code & 0x7F]) 182 | else: 183 | packet = bytearray([ord('K'), 2, 0xF0, code & 0x7F]) 184 | ps2_tcp.sendall(packet) 185 | continue 186 | wheel = 0 187 | if event.type == pygame.MOUSEBUTTONDOWN: # for wheel events 188 | if event.button == 4: # wheel UP 189 | wheel = -1 190 | if event.button == 5: # wheel DOWN 191 | wheel = 1 192 | (dx, dy) = pygame.mouse.get_rel() 193 | dz = wheel 194 | (btn_left, btn_middle, btn_right) = pygame.mouse.get_pressed() 195 | 196 | if mouse_wheel: 197 | # mouse with wheel 198 | report = mouse_wheel_report(dx, dy, dz, btn_left, btn_middle, btn_right) 199 | #print("0x%08X: X=%4d, Y=%4d, Z=%2d, L=%2d, M=%2d, R=%2d" % (struct.unpack("I",report)[0], dx, dy, dz, btn_left, btn_middle, btn_right)) 200 | else: 201 | # mouse without wheel 202 | report = mouse_nowheel_report(dx, dy, btn_left, btn_middle, btn_right) 203 | #print(report) 204 | #print("X=%4d, Y=%4d, L=%2d, M=%2d, R=%2d" % (dx, dy, btn_left, btn_middle, btn_right)) 205 | #ps2_tcp.sendall(bytearray(report)) 206 | -------------------------------------------------------------------------------- /i2c_controller.vhd: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | -- 3 | -- Simple I2C bus interface for initializing the Wolfson WM8731 audio codec 4 | -- on the DE2 5 | -- 6 | -- Stephen A. Edwards (sedwards@cs.columbia.edu) 7 | -- 8 | ------------------------------------------------------------------------------- 9 | library ieee; 10 | use ieee.std_logic_1164.all; 11 | use ieee.numeric_std.all; 12 | 13 | entity i2c_controller is 14 | 15 | port ( 16 | CLK : in std_logic; -- 50 MHz main clock 17 | SCLK : out std_logic; -- I2C clock 18 | SDAT : inout std_logic; -- I2C data 19 | reset : in std_logic); 20 | end i2c_controller; 21 | 22 | architecture rtl of i2c_controller is 23 | 24 | type phases is (IDLE, START, ZERO, ONE, ACK, STOP); 25 | 26 | type packet_states is (P_IDLE, 27 | P_START, 28 | P_ADDR, 29 | P_WRITE, 30 | P_ADDR_ACK, 31 | P_DATA1, 32 | P_DATA1_ACK, 33 | P_DATA2, 34 | P_DATA2_ACK, 35 | P_STOP); 36 | 37 | type data_states is (D_SEND, D_DONE, D_IDLE); 38 | 39 | signal address : unsigned(6 downto 0); 40 | signal data1, data2 : unsigned(7 downto 0); 41 | signal send, done : std_logic; 42 | 43 | begin 44 | 45 | address <= "0011010"; -- fixed address of WM8731 46 | 47 | send_data : process (CLK) 48 | variable state : data_states; 49 | variable reg : unsigned(3 downto 0); 50 | begin 51 | if rising_edge(CLK) then 52 | if reset = '1' then 53 | state := D_DONE; 54 | reg := X"0"; 55 | send <= '0'; 56 | data2 <= X"00"; 57 | else 58 | case state is 59 | when D_DONE => 60 | if done = '0' then 61 | state := D_SEND; 62 | send <= '1'; 63 | data1 <= "000" & reg & "0"; 64 | case reg is 65 | when X"0" => data2 <= X"1A"; -- LIN_L 66 | when X"1" => data2 <= X"1A"; -- LIN_R 67 | when X"2" => data2 <= X"7B"; -- HEAD_L 68 | when X"3" => data2 <= X"7B"; -- HEAD_R 69 | when X"4" => data2 <= X"F8"; -- A_PATH_CTRL 70 | when X"5" => data2 <= X"06"; -- D_PATH_CTRL 71 | when X"6" => data2 <= X"00"; -- POWER_ON 72 | when X"7" => data2 <= X"01"; -- SET_FORMAT 73 | when X"8" => data2 <= X"02"; -- SAMPLE_CTRL 74 | when X"9" => data2 <= X"01"; -- SET_ACTIVE 75 | when others => 76 | state := D_IDLE; 77 | send <= '0'; 78 | end case; 79 | reg := reg + 1; 80 | end if; 81 | 82 | when D_SEND => 83 | if done = '1' then 84 | send <= '0'; 85 | state := D_DONE; 86 | end if; 87 | 88 | when D_IDLE => -- hold 89 | 90 | end case; 91 | end if; 92 | end if; 93 | end process; 94 | 95 | send_packet : process (CLK) 96 | variable clock_prescaler : unsigned(7 downto 0); 97 | variable sreg : unsigned(22 downto 0); 98 | variable state : packet_states; 99 | variable bit_counter : unsigned(2 downto 0); 100 | variable phase : phases; 101 | begin 102 | if rising_edge(CLK) then 103 | if reset = '1' then 104 | state := P_IDLE; 105 | phase := IDLE; 106 | SCLK <= '1'; 107 | SDAT <= 'Z'; 108 | clock_prescaler := X"00"; 109 | sreg := (others => '0'); 110 | else 111 | if clock_prescaler = X"00" then 112 | done <= '0'; 113 | case state is 114 | when P_IDLE => 115 | phase := IDLE; 116 | sreg := address & data1 & data2; 117 | if send = '1' then 118 | state := P_START; 119 | end if; 120 | 121 | when P_START => 122 | phase := START; 123 | bit_counter := "110"; 124 | state := P_ADDR; 125 | 126 | when P_ADDR => 127 | if sreg(22) = '1' then phase := ONE; 128 | else phase := ZERO; end if; 129 | sreg := sreg(21 downto 0) & '0'; 130 | if bit_counter = "000" then state := P_WRITE; end if; 131 | bit_counter := bit_counter - 1; 132 | 133 | when P_WRITE => 134 | phase := ZERO; 135 | state := P_ADDR_ACK; 136 | 137 | when P_ADDR_ACK => 138 | phase := ACK; 139 | bit_counter := "111"; 140 | state := P_DATA1; 141 | 142 | when P_DATA1 => 143 | if sreg(22) = '1' then phase := ONE; 144 | else phase := ZERO; end if; 145 | sreg := sreg(21 downto 0) & '0'; 146 | if bit_counter = "000" then state := P_DATA1_ACK; end if; 147 | bit_counter := bit_counter - 1; 148 | 149 | when P_DATA1_ACK => 150 | phase := ACK; 151 | bit_counter := "111"; 152 | state := P_DATA2; 153 | 154 | when P_DATA2 => 155 | if sreg(22) = '1' then phase := ONE; 156 | else phase := ZERO; end if; 157 | sreg := sreg(21 downto 0) & '0'; 158 | if bit_counter = "000" then state := P_DATA2_ACK; end if; 159 | bit_counter := bit_counter - 1; 160 | 161 | when P_DATA2_ACK => 162 | phase := ACK; 163 | state := P_STOP; 164 | 165 | when P_STOP => 166 | phase := STOP; 167 | done <= '1'; 168 | state := P_IDLE; 169 | 170 | end case; 171 | end if; 172 | 173 | case phase is 174 | when IDLE => 175 | SCLK <= '1'; 176 | SDAT <= 'Z'; 177 | 178 | when START => 179 | if clock_prescaler(7 downto 6) = "00" then SDAT <= '1'; 180 | else SDAT <= '0'; 181 | end if; 182 | if clock_prescaler(7 downto 6) = "11" then SCLK <= '0'; 183 | else SCLK <= '1'; 184 | end if; 185 | 186 | when ZERO | ONE => 187 | if phase = ONE then SDAT <= '1'; else SDAT <= '0'; end if; 188 | if clock_prescaler(7) = clock_prescaler(6) then SCLK <= '0'; 189 | else SCLK <= '1'; 190 | end if; 191 | 192 | when ACK => 193 | if clock_prescaler(7) = clock_prescaler(6) then SCLK <= '0'; 194 | else SCLK <= '1'; end if; 195 | SDAT <= 'Z'; 196 | 197 | when STOP => 198 | SCLK <= '1'; 199 | if clock_prescaler(7) = '1' then SDAT <= '1'; 200 | else SDAT <= '0'; end if; 201 | 202 | end case; 203 | 204 | clock_prescaler := clock_prescaler + 1; 205 | end if; 206 | end if; 207 | end process; 208 | 209 | 210 | 211 | end rtl; 212 | -------------------------------------------------------------------------------- /i2c_testbench.vhd: -------------------------------------------------------------------------------- 1 | library ieee; 2 | use ieee.std_logic_1164.all; 3 | use ieee.numeric_std.all; 4 | 5 | entity i2c_testbench is 6 | 7 | end i2c_testbench; 8 | 9 | architecture behavioral of i2c_testbench is 10 | 11 | signal CLK : std_logic := '0'; 12 | signal reset : std_logic := '1'; 13 | 14 | begin 15 | 16 | uut : entity work.i2c_controller 17 | port map ( 18 | CLK => CLK, 19 | reset => reset 20 | ); 21 | 22 | CLK <= not CLK after 10 ns; 23 | 24 | reset <= '0' after 40 ns; 25 | 26 | end behavioral; 27 | -------------------------------------------------------------------------------- /makenibs: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ $# -ne 2 ] 4 | then 5 | echo "Usage: makenibs " 2>&1 6 | exit 1 7 | fi 8 | 9 | rm -f $2 10 | 11 | IFS=" 12 | " 13 | 14 | i=0 15 | for dsk in `find $1 -name "*.dsk" | sort` 16 | do 17 | awk 'BEGIN {printf("%03x %s\n", ARGV[1], ARGV[2])}' $i "$dsk" 18 | ./dsk2nib "$dsk" tmp.nib 19 | cat tmp.nib >> $2 20 | i=`expr $i + 1` 21 | done 22 | -------------------------------------------------------------------------------- /paddle.vhd: -------------------------------------------------------------------------------- 1 | -- AUTHOR=EMARD 2 | -- LICENSE=BSD 3 | 4 | -- converts digital values to time 5 | -- suitable for PDL signal generation 6 | 7 | -- for PDL reading, APPLE ][ set PDL_STROBE pulse shortly to 1 8 | -- during the pulse, this module sets all PDL bits to 1, 9 | -- after the pulse it starts countdown, 10 | -- when counter expires, 11 | -- corresponding PDL bit is set to 0. 12 | 13 | -- to show paddle value from applesoft: 14 | -- ]?PDL(0) prints paddle 0 15 | -- ]?PDL(1) prints paddle 1 etc. up to 3 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | use ieee.numeric_std.all; 20 | 21 | entity paddle is 22 | generic 23 | ( -- adjust bits and scale so that 24 | -- PDL() reading is equal to digital input. example: 25 | -- ]?PDL(0) reads paddle 0 26 | C_bits : natural := 22; -- larger -> larger PDL() reading 27 | C_scale : natural := 106 -- larger -> smaller PDL() reading 28 | ); 29 | port 30 | ( 31 | CLK_14M : in std_logic; 32 | -- input 8-bit value from joystick 33 | PDL0,PDL1, 34 | PDL2,PDL3 : in std_logic_vector(7 downto 0); -- 8-bit digital input 35 | -- APPLE ][ interface 36 | PDL_STROBE : in std_logic; -- starts counter 37 | GAMEPORT : out std_logic_vector(3 downto 0) -- strobe->1->timeout->0 38 | ); 39 | end; 40 | 41 | architecture rtl of paddle is 42 | signal R_index: unsigned(1 downto 0); -- 4 values for PDL(0) to PDL(3) 43 | signal i: integer; 44 | type T_PDL is array(0 to 3) of unsigned(7 downto 0); 45 | signal S_PDL: T_PDL; 46 | type T_startval is array(0 to 3) of unsigned(C_bits-2 downto C_bits-11); 47 | signal R_startval: T_startval; 48 | type T_paddle is array(0 to 3) of unsigned(C_bits-1 downto 0); 49 | signal R_paddle: T_paddle; 50 | begin 51 | S_PDL(0) <= unsigned(PDL0); 52 | S_PDL(1) <= unsigned(PDL1); 53 | S_PDL(2) <= unsigned(PDL2); 54 | S_PDL(3) <= unsigned(PDL3); 55 | -- simulate analog conversion from value to time 56 | i <= to_integer(R_index); 57 | process(CLK_14M) 58 | begin 59 | if rising_edge(CLK_14M) then 60 | R_startval(i) <= ("0" & S_PDL(i) & "0"); -- + C_offset; 61 | if PDL_STROBE = '1' then 62 | R_paddle(i)(C_bits-1) <= '1'; 63 | R_paddle(i)(C_bits-2 downto C_bits-11) <= R_startval(i); 64 | R_paddle(i)(C_bits-12 downto 0) <= (others => '0'); 65 | else 66 | if R_paddle(i)(C_bits-1) = '1' then 67 | R_paddle(i) <= R_paddle(i) - C_scale; 68 | end if; 69 | end if; 70 | R_index <= R_index + 1; 71 | end if; 72 | end process; 73 | G_output: for j in 0 to 3 generate 74 | GAMEPORT(j) <= R_paddle(j)(C_bits-1); 75 | end generate; 76 | end rtl; 77 | -------------------------------------------------------------------------------- /proj/lattice/ffmlfe5/makefile: -------------------------------------------------------------------------------- 1 | # ******* project, board and chip name ******* 2 | PROJECT = apple2 3 | BOARD = ffmlfe5 4 | # 12 25 45 85 5 | FPGA_SIZE = 85 6 | 7 | FPGA_PACKAGE = 6bg554c 8 | # config flash: 1:SPI (standard), 4:QSPI (quad) 9 | FLASH_SPI = 4 10 | # chip: is25lp032d is25lp128f s25fl164k 11 | FLASH_CHIP = is25lp128f 12 | 13 | # ******* if programming with OpenOCD ******* 14 | # using local latest openocd until in linux distribution 15 | OPENOCD=openocd 16 | # default onboard usb-jtag 17 | OPENOCD_INTERFACE=$(SCRIPTS)/ft4232.ocd 18 | 19 | # ******* design files ******* 20 | CONSTRAINTS = ../constraints/FFM-LFE5U-V0r0_mit_FFC-CA7-V2r0.lpf 21 | #TOP_MODULE = ulx3s_apple2 22 | TOP_MODULE = ffmlfe5_apple2 23 | TOP_MODULE_FILE = ../../../rtl_emard/lattice/top/$(TOP_MODULE).vhd 24 | 25 | CLK3_NAME = clk_sys 26 | CLK3_FILE_NAME = ../../../rtl_emard/lattice/ecp5/clocks/$(CLK3_NAME).v 27 | CLK3_OPTIONS = \ 28 | --module=$(CLK3_NAME) \ 29 | --clkin=100 \ 30 | --clkout0_name=clk_25 --clkout0=25 31 | 32 | VERILOG_FILES = $(CLK3_FILE_NAME) 33 | 34 | VHDL_FILES = \ 35 | $(TOP_MODULE_FILE) \ 36 | ../../../rtl_emard/lattice/ecp5/clocks/clk_sys_vhdl.vhd \ 37 | ../../../rtl_emard/lattice/top/ulx3s_apple2.vhd \ 38 | ../../../rtl_emard/lattice/ecp5/clocks/clk_25_140_28_14.vhd \ 39 | ../../../rtl_emard/oled/oled_hex_decoder.vhd \ 40 | ../../../rtl_emard/oled/oled_init_pack.vhd \ 41 | ../../../rtl_emard/oled/oled_font_pack.vhd \ 42 | ../../../rtl_emard/bram/bram_true2p_1clk.vhd \ 43 | ../../../rtl_emard/dvi/vga2dvid.vhd \ 44 | ../../../rtl_emard/dvi/tmds_encoder.vhd \ 45 | ../../../apple2.vhd \ 46 | ../../../main_roms.vhd \ 47 | ../../../character_rom.vhd \ 48 | ../../../cpu6502.vhd \ 49 | ../../../keyboard.vhd \ 50 | ../../../PS2_Ctrl.vhd \ 51 | ../../../timing_generator.vhd \ 52 | ../../../vga_controller.vhd \ 53 | ../../../video_generator.vhd \ 54 | ../../../disk_ii.vhd \ 55 | ../../../disk_ii_rom.vhd \ 56 | ../../../spi_controller.vhd \ 57 | ../../../disk2_spi_slave.vhd \ 58 | 59 | SCRIPTS = ../scripts 60 | include $(SCRIPTS)/trellis_path.mk 61 | include $(SCRIPTS)/diamond_path.mk 62 | include $(SCRIPTS)/diamond_main.mk 63 | -------------------------------------------------------------------------------- /proj/lattice/scripts/IspXCF.dtd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emard/apple2fpga/0ad84ae2f9d9d5cc0e8db6128d630a9e16cdd6de/proj/lattice/scripts/IspXCF.dtd -------------------------------------------------------------------------------- /proj/lattice/scripts/diamond_path.mk: -------------------------------------------------------------------------------- 1 | # the path of your diamond installation 2 | DIAMOND_BASE ?= /usr/local/diamond 3 | 4 | # it is a directory that looks like this: 5 | # ls /usr/local/diamond 6 | # 3.7_x64 7 | -------------------------------------------------------------------------------- /proj/lattice/scripts/ecp5-ocd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ecp5-ocd.sh 3 | 4 | CHIP_ID=$1 5 | FILE_SVF=$2 6 | 7 | cat << EOF 8 | # OpenOCD commands 9 | 10 | telnet_port 4444 11 | gdb_port 3333 12 | 13 | # JTAG TAPs 14 | jtag newtap lfe5 tap -expected-id ${CHIP_ID} -irlen 8 -irmask 0xFF -ircapture 0x5 15 | 16 | init 17 | scan_chain 18 | svf -tap lfe5.tap -quiet -progress ${FILE_SVF} 19 | shutdown 20 | EOF 21 | -------------------------------------------------------------------------------- /proj/lattice/scripts/ft2232.ocd: -------------------------------------------------------------------------------- 1 | # 2 | # Generic FT2232H JTAG Programmer 3 | # 4 | 5 | adapter driver ftdi 6 | # ftdi_device_desc "Dual RS232-HS" 7 | ftdi_vid_pid 0x0403 0x6010 8 | ftdi_layout_init 0x3088 0x1f8b 9 | 10 | # default is port A if unspecified 11 | # pinout ADBUS 0-TCK 1-TDI 2-TDO 3-TMS 12 | #ftdi_channel 0 13 | 14 | # uncomment this to use port B 15 | # pinout BDBUS 0-TCK 1-TDI 2-TDO 3-TMS 16 | #ftdi_channel 1 17 | 18 | adapter speed 25000 19 | -------------------------------------------------------------------------------- /proj/lattice/scripts/ft231x.ocd: -------------------------------------------------------------------------------- 1 | # 2 | # openocd_ft232r patched for custom jtag nums and buffer_size 3 | # 4 | 5 | adapter driver ft232r 6 | ft232r_vid_pid 0x0403 0x6015 7 | # ft232r_serial_desc 123456 8 | ft232r_tck_num DSR 9 | ft232r_tms_num DCD 10 | ft232r_tdi_num RI 11 | ft232r_tdo_num CTS 12 | ft232r_trst_num RTS 13 | ft232r_srst_num DTR 14 | ft232r_restore_serial 0x15 15 | adapter speed 1000 16 | -------------------------------------------------------------------------------- /proj/lattice/scripts/ft231x2.ocd: -------------------------------------------------------------------------------- 1 | # 2 | # openocd_ft232r patched for custom jtag nums and buffer_size 3 | # pinout for ulx3s jtag-thru: 4 | 5 | # GP14 TMS 6 | # GN14 TDO 7 | # GP15 TDI 8 | # GN15 TCK 9 | 10 | adapter driver ft232r 11 | ft232r_vid_pid 0x0403 0x6015 12 | # ft232r_serial_desc 250001 13 | ft232r_tck_num DTR 14 | ft232r_tms_num RTS 15 | ft232r_tdi_num TXD 16 | ft232r_tdo_num RXD 17 | ft232r_trst_num DCD 18 | ft232r_srst_num RI 19 | adapter speed 1000 20 | -------------------------------------------------------------------------------- /proj/lattice/scripts/ft232r.ocd: -------------------------------------------------------------------------------- 1 | # 2 | # openocd_ft232r patched for custom jtag nums and buffer_size 3 | # low-cost FT232R board from ebay 4 | 5 | interface ft232r 6 | # ft232r_vid_pid 0x0403 0x6015 7 | # ft232r_serial_desc 250001 8 | ft232r_tck_num DTR 9 | ft232r_tms_num CTS 10 | ft232r_tdi_num TXD 11 | ft232r_tdo_num RXD 12 | ft232r_trst_num DCD 13 | ft232r_srst_num RI 14 | adapter speed 1000 15 | -------------------------------------------------------------------------------- /proj/lattice/scripts/ft4232.ocd: -------------------------------------------------------------------------------- 1 | # 2 | # Generic FT4232 JTAG Programmer 3 | # 4 | 5 | adapter driver ftdi 6 | # ftdi_device_desc "Quad RS232-HS" 7 | ftdi_vid_pid 0x0403 0x6011 8 | ftdi_layout_init 0x3088 0x1f8b 9 | 10 | # default is port A if unspecified 11 | # pinout ADBUS 0-TCK 1-TDI 2-TDO 3-TMS 12 | #ftdi_channel 0 13 | 14 | # uncomment this to use port B 15 | # pinout BDBUS 0-TCK 1-TDI 2-TDO 3-TMS 16 | #ftdi_channel 1 17 | 18 | adapter speed 25000 19 | -------------------------------------------------------------------------------- /proj/lattice/scripts/ldf.xsl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /proj/lattice/scripts/project.ldf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /proj/lattice/scripts/trellis_path.mk: -------------------------------------------------------------------------------- 1 | # ******* tools installation paths ******* 2 | # https://github.com/SymbiFlow/prjtrellis 3 | TRELLIS ?= /mt/scratch/tmp/openfpga/prjtrellis 4 | # https://github.com/ldoolitt/vhd2vl 5 | VHDL2VL ?= vhd2vl 6 | # https://github.com/YosysHQ/yosys 7 | YOSYS ?= yosys 8 | # https://github.com/YosysHQ/nextpnr 9 | NEXTPNR-ECP5 ?= nextpnr-ecp5 10 | # trellis ecppack 11 | ECPPACK ?= LANG=C ecppack 12 | ECPPLL ?= LANG=C ecppll 13 | -------------------------------------------------------------------------------- /proj/lattice/scripts/ulx3s_flash_is25lp032d.xcf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | JTAG 7 | 8 | 9 | 1 10 | Lattice 11 | ECP5U 12 | LFE5U-12F 13 | All 14 | 15 | 8 16 | 11111111 17 | 1 18 | 0 19 | 20 | project/project_project_flash_is25lp032d.mcs 21 | 12/23/17 00:34:50 22 | SPI Flash Erase,Program 23 | 27 | 28 | 29 | 30 | 1 31 | Lattice 32 | ECP5U 33 | LFE5U-12F 34 | 0x21111043 35 | All 36 | LFE5U-12F 37 | 38 | 8 39 | 11111111 40 | 1 41 | 0 42 | 43 | /mt/lattice/diamond/3.7_x64/data/vmdata/database/xpga/ecp5/LFE5U-45F.msk 44 | Bypass 45 | 52 | 53 | 54 | 55 | 56 | 1 57 | Micron 58 | SPI Serial Flash 59 | SPI-M25P32 60 | 0x15 61 | 8-pin VDFPN8 62 | SPI Flash Erase,Program 63 | project/project_project_flash_is25lp032d.mcs 64 | 0x00000000 65 | 0x00400000 66 | 32 67 | 4194304 68 | 1 69 | 70 | 71 | 72 | 73 | 74 | 1 75 | 76 | project/project_project_flash_is25lp032d.mcs 77 | 78 | 81 | 82 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | SEQUENTIAL 92 | ENTIRED CHAIN 93 | No Override 94 | TLR 95 | TLR 96 | 97 | 1 98 | 99 | 100 | USB2 101 | FTUSB-0 102 | FPU1 JTAG PROGRAMMER A Location 0000 Serial FPU1 JTAG Programmer A 103 | 104 | 105 | -------------------------------------------------------------------------------- /proj/lattice/scripts/ulx3s_flash_is25lp128f.xcf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | JTAG 7 | 8 | 9 | 1 10 | Lattice 11 | ECP5U 12 | LFE5U-12F 13 | All 14 | 15 | 8 16 | 11111111 17 | 1 18 | 0 19 | 20 | project/project_project_flash_is25lp128f.mcs 21 | 12/23/17 00:34:50 22 | SPI Flash Erase,Program 23 | 27 | 28 | 29 | 30 | 1 31 | Lattice 32 | ECP5U 33 | LFE5U-12F 34 | 0x21111043 35 | All 36 | LFE5U-12F 37 | 38 | 8 39 | 11111111 40 | 1 41 | 0 42 | 43 | /mt/lattice/diamond/3.7_x64/data/vmdata/database/xpga/ecp5/LFE5U-45F.msk 44 | Bypass 45 | 52 | 53 | 54 | 55 | 56 | 1 57 | SPANSION 58 | SPI Serial Flash 59 | SPI-S25FL128S 60 | 8-lead WSON 61 | 0x17 62 | SPI Flash Erase,Program 63 | project/project_project_flash_is25lp128f.mcs 64 | 0x00000000 65 | 0x01000000 66 | 128 67 | 16777216 68 | 1 69 | 70 | 71 | 72 | 73 | 74 | 1 75 | 76 | project/project_project_flash_is25lp128f.mcs 77 | 78 | 81 | 82 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | SEQUENTIAL 92 | ENTIRED CHAIN 93 | No Override 94 | TLR 95 | TLR 96 | 97 | 1 98 | 99 | 100 | USB2 101 | FTUSB-0 102 | FPU1 JTAG PROGRAMMER A Location 0000 Serial FPU1 JTAG Programmer A 103 | 104 | 105 | -------------------------------------------------------------------------------- /proj/lattice/scripts/ulx3s_flash_s25fl164k.xcf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | JTAG 7 | 8 | 9 | 1 10 | Lattice 11 | ECP5U 12 | LFE5U-12F 13 | All 14 | 15 | 8 16 | 11111111 17 | 1 18 | 0 19 | 20 | project/project_project_flash_s25fl164k.mcs 21 | 12/23/17 00:34:50 22 | SPI Flash Erase,Program 23 | 27 | 28 | 29 | 30 | 1 31 | Lattice 32 | ECP5U 33 | LFE5U-12F 34 | 0x21111043 35 | All 36 | LFE5U-12F 37 | 38 | 8 39 | 11111111 40 | 1 41 | 0 42 | 43 | /mt/lattice/diamond/3.7_x64/data/vmdata/database/xpga/ecp5/LFE5U-45F.msk 44 | Bypass 45 | 52 | 53 | 54 | 55 | 56 | 1 57 | SPANSION 58 | SPI Serial Flash 59 | SPI-S25FL164K 60 | 0x16 61 | 8-lead SOIC 62 | SPI Flash Erase,Program 63 | project/project_project_flash_s25fl164k.mcs 64 | 0x00000000 65 | 0x00800000 66 | 64 67 | 8388608 68 | 1 69 | 70 | 71 | 72 | 73 | 74 | 1 75 | 76 | project/project_project_flash_s25fl164k.mcs 77 | 78 | 81 | 82 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | SEQUENTIAL 92 | ENTIRED CHAIN 93 | No Override 94 | TLR 95 | TLR 96 | 97 | 1 98 | 99 | 100 | USB2 101 | FTUSB-0 102 | FPU1 JTAG PROGRAMMER A Location 0000 Serial FPU1 JTAG Programmer A 103 | 104 | 105 | -------------------------------------------------------------------------------- /proj/lattice/scripts/ulx3s_sram.xcf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | JTAG 7 | 8 | 9 | 1 10 | Lattice 11 | ECP5U 12 | LFE5U-25F 13 | 0x41111043 14 | All 15 | LFE5U-25F 16 | 17 | 8 18 | 11111111 19 | 1 20 | 0 21 | 22 | project/project_project.bit 23 | Fast Program 24 | 32 | 33 | 34 | 35 | SEQUENTIAL 36 | ENTIRED CHAIN 37 | No Override 38 | TLR 39 | TLR 40 | 41 | 1 42 | 43 | 44 | USB2 45 | FTUSB-0 46 | DUAL RS232-HS A Location 0000 Serial Dual RS232-HS A 47 | 48 | 49 | -------------------------------------------------------------------------------- /proj/lattice/scripts/xcf.xsl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /proj/lattice/ulx3s/Makefile: -------------------------------------------------------------------------------- 1 | # ******* project, board and chip name ******* 2 | PROJECT = apple2 3 | BOARD = ulx3s 4 | # 12 25 45 85 5 | FPGA_SIZE = 85 6 | 7 | FPGA_PACKAGE = 6bg381c 8 | # config flash: 1:SPI (standard), 4:QSPI (quad) 9 | FLASH_SPI = 4 10 | # chip: is25lp032d is25lp128f s25fl164k 11 | FLASH_CHIP = is25lp128f 12 | 13 | # ******* if programming with OpenOCD ******* 14 | # using local latest openocd until in linux distribution 15 | OPENOCD=openocd_ft232r 16 | # default onboard usb-jtag 17 | OPENOCD_INTERFACE=$(SCRIPTS)/ft231x.ocd 18 | # ulx3s-jtag-passthru 19 | #OPENOCD_INTERFACE=$(SCRIPTS)/ft231x2.ocd 20 | # ulx2s 21 | #OPENOCD_INTERFACE=$(SCRIPTS)/ft232r.ocd 22 | # external jtag 23 | #OPENOCD_INTERFACE=$(SCRIPTS)/ft2232.ocd 24 | 25 | # ******* design files ******* 26 | CONSTRAINTS = ../constraints/ulx3s_v20_difgpdi.lpf 27 | TOP_MODULE = ulx3s_v20_apple2 28 | #CONSTRAINTS = ../constraints/ulx3s_v31.lpf 29 | #TOP_MODULE = ulx3s_v31_apple2 30 | TOP_MODULE_FILE = ../../../rtl_emard/lattice/top/$(TOP_MODULE).vhd 31 | 32 | VHDL_FILES = \ 33 | $(TOP_MODULE_FILE) \ 34 | ../../../rtl_emard/lattice/ecp5/clocks/ecp5pll.vhd \ 35 | ../../../rtl_emard/oled/oled_hex_decoder.vhd \ 36 | ../../../rtl_emard/oled/oled_init_pack.vhd \ 37 | ../../../rtl_emard/oled/oled_font_pack.vhd \ 38 | ../../../rtl_emard/osd/osd_vhd.vhd \ 39 | ../../../rtl_emard/osd/spi_ram_btn.vhd \ 40 | ../../../rtl_emard/spi_display/spi_display.vhd \ 41 | ../../../rtl_emard/spi_display/spi_display_init_pack.vhd \ 42 | ../../../rtl_emard/spi_display/st7789_init_pack.vhd \ 43 | ../../../rtl_emard/bram/bram_true2p_1clk.vhd \ 44 | ../../../rtl_emard/dvi/vga2dvid.vhd \ 45 | ../../../rtl_emard/dvi/tmds_encoder.vhd \ 46 | ../../../rtl_emard/usb/usb11_phy_vhdl/usb_phy.vhd \ 47 | ../../../rtl_emard/usb/usb11_phy_vhdl/usb_rx_phy.vhd \ 48 | ../../../rtl_emard/usb/usb11_phy_vhdl/usb_tx_phy.vhd \ 49 | ../../../rtl_emard/usb/usbhost/usbh_setup_pack.vhd \ 50 | ../../../rtl_emard/usb/usbhost/usbh_host_hid.vhd \ 51 | ../../../rtl_emard/usb/usbhost/usbh_sie_vhdl.vhd \ 52 | ../../../rtl_emard/usb/usbhid/report_decoded_pack_generic.vhd \ 53 | ../../../rtl_emard/usb/usbhid/usbhid_report_decoder_darfon_joystick.vhd \ 54 | ../../../apple2.vhd \ 55 | ../../../timing_generator.vhd \ 56 | ../../../main_roms.vhd \ 57 | ../../../character_rom.vhd \ 58 | ../../../cpu6502.vhd \ 59 | ../../../keyboard.vhd \ 60 | ../../../PS2_Ctrl.vhd \ 61 | ../../../paddle.vhd \ 62 | ../../../vga_controller.vhd \ 63 | ../../../video_generator.vhd \ 64 | ../../../disk_ii.vhd \ 65 | ../../../disk_ii_rom.vhd \ 66 | ../../../spi_controller.vhd \ 67 | ../../../rtl_emard/osd/spi_osd.vhd \ 68 | ../../../rtl_emard/osd/spirw_slave.vhd \ 69 | 70 | # clocks/clk_25_shift_pixel.vhd \ 71 | 72 | VERILOG_FILES = \ 73 | ../../../rtl_emard/osd/spi_osd_v.v \ 74 | ../../../rtl_emard/osd/osd.v \ 75 | ../../../rtl_emard/osd/spi_ram_btn_v.v \ 76 | ../../../rtl_emard/osd/spirw_slave_v.v \ 77 | ../../../rtl_emard/usb/usbhost/usbh_sie.v \ 78 | ../../../rtl_emard/usb/usbhost/usbh_crc5.v \ 79 | ../../../rtl_emard/usb/usbhost/usbh_crc16.v \ 80 | 81 | # $(CLK0_FILE_NAME) \ 82 | 83 | SCRIPTS = ../scripts 84 | include $(SCRIPTS)/trellis_path.mk 85 | include $(SCRIPTS)/diamond_path.mk 86 | include $(SCRIPTS)/diamond_main.mk 87 | 88 | osd.mem: osd.txt 89 | hexdump -n 1280 -v -e '1/1 "%02x " "\n"' $< > $@ 90 | -------------------------------------------------------------------------------- /proj/lattice/ulx3s/README.md: -------------------------------------------------------------------------------- 1 | # ULX3S APPLE ][ 2 | 3 | It boots and even supports read-only floopy images 4 | in NIB format. 5 | 6 | # compiling 7 | 8 | Edit "Makefile" and select FPGA size 25/45/85 9 | 10 | make clean; make prog 11 | 12 | # usage 13 | 14 | Raw floppy images can be written to SD card with ESP32 uftpd.py 15 | 16 | ftp> put disk2.nib sd@0 17 | 18 | Or ESP32 osd.py server and the image file file can be put 19 | ESP32 FLASH filesystem: 20 | 21 | ftp> put osd.py 22 | ftp> put disk2.nib 23 | 24 | and ESP32 DISK server started: 25 | 26 | screen /dev/ttyUSB0 115200 27 | >>> import osd 28 | import osd 29 | 30 | press 4 direction buttons together (BTN3-6) or BTN 1 31 | to open OSD menu, select file disk2.nib by pressing right direction (BTN6) 32 | and it will "insert" disk2.nib floppy into emulated drive. 33 | 34 | Some apple2 hints: 35 | 36 | ]CATALOG 37 | ]PR#6 38 | ]CALL -151 39 | *6 40 | *9DBFG 41 | ] 42 | 43 | Most floppy disk images from [planet emulation](https://www.planetemu.net/machine/apple-ii) 44 | are in DSK format. 45 | [online disk image converter at kboohk](http://kboohk.com/dsk2woz/) 46 | can convert them to NIB format. 47 | -------------------------------------------------------------------------------- /proj/lattice/ulx3s/clocks/clk_25_shift_pixel.vhd: -------------------------------------------------------------------------------- 1 | -- VHDL wrapper for clock 2 | 3 | library ieee; 4 | use ieee.std_logic_1164.all; 5 | use ieee.std_logic_unsigned.all; 6 | use ieee.std_logic_arith.all; 7 | 8 | entity clk_25_shift_pixel is 9 | port 10 | ( 11 | clki: in std_logic; 12 | clko,clks1,clks2,locked: out std_logic 13 | ); 14 | end; 15 | 16 | architecture syn of clk_25_shift_pixel is 17 | component clk_25_shift_pixel_v -- verilog name and its parameters 18 | port 19 | ( 20 | clki: in std_logic; 21 | clko,clks1,clks2,locked: out std_logic 22 | ); 23 | end component; 24 | 25 | begin 26 | clk_25_shift_pixel_v_inst: clk_25_shift_pixel_v 27 | port map 28 | ( 29 | clki => clki, 30 | clko => clko, clks1 => clks1, clks2 => clks2, locked => locked 31 | ); 32 | end syn; 33 | -------------------------------------------------------------------------------- /proj/lattice/ulx3s/font_bizcat8x16.mem: -------------------------------------------------------------------------------- 1 | ../../../rtl_emard/osd/font_bizcat8x16.mem -------------------------------------------------------------------------------- /proj/lattice/ulx3s/osd.mem: -------------------------------------------------------------------------------- 1 | 4f 2 | 53 3 | 44 4 | 20 5 | 28 6 | 6f 7 | 6e 8 | 2d 9 | 73 10 | 63 11 | 72 12 | 65 13 | 65 14 | 6e 15 | 20 16 | 64 17 | 69 18 | 73 19 | 70 20 | 6c 21 | 61 22 | 79 23 | 29 24 | 20 25 | 76 26 | 69 27 | 64 28 | 65 29 | 6f 30 | 20 31 | 32 | -------------------------------------------------------------------------------- /proj/lattice/ulx3s/osd.txt: -------------------------------------------------------------------------------- 1 | *************************************************************** 2 | * OSD (on-screen display) video overlay soft core can display 3 | * ASCII character map over the video display by modifying VGA 4 | * pixel stream. In rectangular screen area, video content is 5 | * replaced with pixels coming from ASCII font, forming a 6 | * "window". This can be combined with a SPI slave receiver and 7 | * used to provide screen display for external CPU. For example, 8 | * ESP32 micropython code can "print" on OSD screen. This method 9 | * can be used to make SD card file selection menu. 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | *************************************************************** 21 | -------------------------------------------------------------------------------- /rom2vhdl: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # A shell script for generating main_roms.vhd and disk_ii_rom.vhd 4 | # 5 | # Usage: 6 | # 7 | # ./rom2vhdl main_roms 13 12287 < apple_II.rom > main_roms.vhd 8 | # 9 | # ./rom2vhdl disk_ii_rom 7 255 < slot6.rom > disk_ii_rom.vhd 10 | 11 | echo "library ieee; 12 | use ieee.std_logic_1164.all; 13 | use ieee.numeric_std.all; 14 | 15 | entity $1 is 16 | port ( 17 | addr : in unsigned($2 downto 0); 18 | clk : in std_logic; 19 | dout : out unsigned(7 downto 0)); 20 | end $1; 21 | 22 | architecture rtl of $1 is 23 | type rom_array is array(0 to $3) of unsigned(7 downto 0); 24 | 25 | constant ROM : rom_array := (" 26 | 27 | od --format x1 --address-radix=n --output-duplicates --width=8 | awk 'NR > 1 { printf(",\n") } 28 | {printf " X\"%s\", X\"%s\", X\"%s\", X\"%s\", X\"%s\", X\"%s\", X\"%s\", X\"%s\"", $1, $2, $3, $4, $5, $6, $7, $8}' 29 | 30 | echo "); 31 | 32 | begin 33 | 34 | process (clk) 35 | begin 36 | if rising_edge(clk) then 37 | dout <= ROM(TO_INTEGER(addr)); 38 | end if; 39 | end process; 40 | 41 | end rtl;" 42 | -------------------------------------------------------------------------------- /rtl_emard/bram/bram_true2p_1clk.vhd: -------------------------------------------------------------------------------- 1 | -- Generated by Quartus II Template 2 | 3 | -- File->New File->VHDL File 4 | -- Edit->Insert Template->VHDL->Full designs->RAMs and ROMs->True dual port RAM (singled clock) 5 | 6 | -- True Dual-Port RAM with single clock 7 | 8 | -- when pass_thru enabled on port 9 | -- then Read-during-write on port should return newly written data 10 | 11 | library ieee; 12 | use ieee.std_logic_1164.all; 13 | use ieee.std_logic_unsigned.all; 14 | 15 | entity bram_true2p_1clk is 16 | generic 17 | ( 18 | dual_port: boolean := True; -- set to False for single port A 19 | pass_thru_a, pass_thru_b: boolean := True; 20 | data_width: natural := 8; 21 | addr_width: natural := 6 22 | ); 23 | port 24 | ( 25 | clk: in std_logic; 26 | addr_a: in std_logic_vector((addr_width-1) downto 0); 27 | addr_b: in std_logic_vector((addr_width-1) downto 0) := (others => '-'); 28 | we_a: in std_logic := '0'; 29 | we_b: in std_logic := '0'; 30 | data_in_a: in std_logic_vector((data_width-1) downto 0); 31 | data_in_b: in std_logic_vector((data_width-1) downto 0) := (others => '-'); 32 | data_out_a: out std_logic_vector((data_width -1) downto 0); 33 | data_out_b: out std_logic_vector((data_width -1) downto 0) 34 | ); 35 | end bram_true2p_1clk; 36 | 37 | architecture rtl of bram_true2p_1clk is 38 | -- Build a 2-D array type for the RAM 39 | subtype word_t is std_logic_vector((data_width-1) downto 0); 40 | type memory_t is array(2**addr_width-1 downto 0) of word_t; 41 | 42 | -- Declare the RAM 43 | shared variable ram: memory_t; 44 | begin 45 | -- Port A 46 | G_port_a_passthru: if pass_thru_a generate 47 | process(clk) 48 | begin 49 | if(rising_edge(clk)) then 50 | if(we_a = '1') then 51 | ram(conv_integer(addr_a)) := data_in_a; 52 | end if; 53 | data_out_a <= ram(conv_integer(addr_a)); 54 | end if; 55 | end process; 56 | end generate; 57 | 58 | G_port_a_not_passthru: if not pass_thru_a generate 59 | process(clk) 60 | begin 61 | if(rising_edge(clk)) then 62 | data_out_a <= ram(conv_integer(addr_a)); 63 | if(we_a = '1') then 64 | ram(conv_integer(addr_a)) := data_in_a; 65 | end if; 66 | end if; 67 | end process; 68 | end generate; 69 | 70 | -- Port B 71 | G_port_b_passthru: if dual_port and pass_thru_b generate 72 | process(clk) 73 | begin 74 | if(rising_edge(clk)) then 75 | if(we_b = '1') then 76 | ram(conv_integer(addr_b)) := data_in_b; 77 | end if; 78 | data_out_b <= ram(conv_integer(addr_b)); 79 | end if; 80 | end process; 81 | end generate; 82 | 83 | G_port_b_not_passthru: if dual_port and not pass_thru_b generate 84 | process(clk) 85 | begin 86 | if(rising_edge(clk)) then 87 | data_out_b <= ram(conv_integer(addr_b)); 88 | if(we_b = '1') then 89 | ram(conv_integer(addr_b)) := data_in_b; 90 | end if; 91 | end if; 92 | end process; 93 | end generate; 94 | end rtl; 95 | -------------------------------------------------------------------------------- /rtl_emard/dvi/tmds_encoder.vhd: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------------------- 2 | -- Engineer: Mike Field 3 | -- 4 | -- Description: TMDS Encoder 5 | -- 8 bits colour, 2 control bits and one blanking bits in 6 | -- 10 bits of TMDS encoded data out 7 | -- Clocked at the pixel clock 8 | -- 9 | ---------------------------------------------------------------------------------- 10 | -- See: http://hamsterworks.co.nz/mediawiki/index.php/Dvid_test 11 | -- http://hamsterworks.co.nz/mediawiki/index.php/FPGA_Projects 12 | -- 13 | -- Copyright (c) 2012 Mike Field 14 | -- 15 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 16 | -- of this software and associated documentation files (the "Software"), to deal 17 | -- in the Software without restriction, including without limitation the rights 18 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | -- copies of the Software, and to permit persons to whom the Software is 20 | -- furnished to do so, subject to the following conditions: 21 | -- 22 | -- The above copyright notice and this permission notice shall be included in 23 | -- all copies or substantial portions of the Software. 24 | -- 25 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31 | -- THE SOFTWARE. 32 | -- 33 | 34 | library IEEE; 35 | use IEEE.STD_LOGIC_1164.ALL; 36 | use IEEE.STD_LOGIC_UNSIGNED.ALL; 37 | 38 | entity tmds_encoder is 39 | Port ( clk : in STD_LOGIC; 40 | data : in STD_LOGIC_VECTOR (7 downto 0); 41 | c : in STD_LOGIC_VECTOR (1 downto 0); 42 | blank : in STD_LOGIC; 43 | encoded : out STD_LOGIC_VECTOR (9 downto 0)); 44 | end tmds_encoder; 45 | 46 | architecture Behavioral of tmds_encoder is 47 | signal xored : STD_LOGIC_VECTOR (8 downto 0); 48 | signal xnored : STD_LOGIC_VECTOR (8 downto 0); 49 | 50 | signal ones : STD_LOGIC_VECTOR (3 downto 0); 51 | signal data_word : STD_LOGIC_VECTOR (8 downto 0); 52 | signal data_word_inv : STD_LOGIC_VECTOR (8 downto 0); 53 | signal data_word_disparity : STD_LOGIC_VECTOR (3 downto 0); 54 | signal dc_bias : STD_LOGIC_VECTOR (3 downto 0) := (others => '0'); 55 | begin 56 | -- Work our the two different encodings for the byte 57 | xored(0) <= data(0); 58 | xored(1) <= data(1) xor xored(0); 59 | xored(2) <= data(2) xor xored(1); 60 | xored(3) <= data(3) xor xored(2); 61 | xored(4) <= data(4) xor xored(3); 62 | xored(5) <= data(5) xor xored(4); 63 | xored(6) <= data(6) xor xored(5); 64 | xored(7) <= data(7) xor xored(6); 65 | xored(8) <= '1'; 66 | 67 | xnored(0) <= data(0); 68 | xnored(1) <= data(1) xnor xnored(0); 69 | xnored(2) <= data(2) xnor xnored(1); 70 | xnored(3) <= data(3) xnor xnored(2); 71 | xnored(4) <= data(4) xnor xnored(3); 72 | xnored(5) <= data(5) xnor xnored(4); 73 | xnored(6) <= data(6) xnor xnored(5); 74 | xnored(7) <= data(7) xnor xnored(6); 75 | xnored(8) <= '0'; 76 | 77 | -- Count how many ones are set in data 78 | ones <= "0000" + data(0) + data(1) + data(2) + data(3) 79 | + data(4) + data(5) + data(6) + data(7); 80 | 81 | -- Decide which encoding to use 82 | process(ones, data(0), xnored, xored) 83 | begin 84 | if ones > 4 or (ones = 4 and data(0) = '0') then 85 | data_word <= xnored; 86 | data_word_inv <= NOT(xnored); 87 | else 88 | data_word <= xored; 89 | data_word_inv <= NOT(xored); 90 | end if; 91 | end process; 92 | 93 | -- Work out the DC bias of the dataword; 94 | data_word_disparity <= "1100" + data_word(0) + data_word(1) + data_word(2) + data_word(3) 95 | + data_word(4) + data_word(5) + data_word(6) + data_word(7); 96 | 97 | -- Now work out what the output should be 98 | process(clk) 99 | begin 100 | if rising_edge(clk) then 101 | if blank = '1' then 102 | -- In the control periods, all values have and have balanced bit count 103 | case c is 104 | when "00" => encoded <= "1101010100"; 105 | when "01" => encoded <= "0010101011"; 106 | when "10" => encoded <= "0101010100"; 107 | when others => encoded <= "1010101011"; 108 | end case; 109 | dc_bias <= (others => '0'); 110 | else 111 | if dc_bias = "00000" or data_word_disparity = 0 then 112 | -- dataword has no disparity 113 | if data_word(8) = '1' then 114 | encoded <= "01" & data_word(7 downto 0); 115 | dc_bias <= dc_bias + data_word_disparity; 116 | else 117 | encoded <= "10" & data_word_inv(7 downto 0); 118 | dc_bias <= dc_bias - data_word_disparity; 119 | end if; 120 | elsif (dc_bias(3) = '0' and data_word_disparity(3) = '0') or 121 | (dc_bias(3) = '1' and data_word_disparity(3) = '1') then 122 | encoded <= '1' & data_word(8) & data_word_inv(7 downto 0); 123 | dc_bias <= dc_bias + data_word(8) - data_word_disparity; 124 | else 125 | encoded <= '0' & data_word; 126 | dc_bias <= dc_bias - data_word_inv(8) + data_word_disparity; 127 | end if; 128 | end if; 129 | end if; 130 | end process; 131 | end Behavioral; 132 | -------------------------------------------------------------------------------- /rtl_emard/lattice/ecp5/clocks/clk_sys_vhdl.vhd: -------------------------------------------------------------------------------- 1 | -- 2 | -- AUTHOR=EMARD 3 | -- LICENSE=BSD 4 | -- 5 | 6 | -- VHDL Wrapper 7 | 8 | LIBRARY ieee; 9 | USE ieee.std_logic_1164.all; 10 | use ieee.std_logic_unsigned.all; 11 | use ieee.std_logic_arith.all; 12 | 13 | entity clk_sys_vhdl is 14 | port 15 | ( 16 | clkin : in std_logic; 17 | clk_25 : out std_logic; 18 | locked : out std_logic 19 | ); 20 | end; 21 | 22 | architecture syn of clk_sys_vhdl is 23 | component clk_sys -- verilog name and its parameters 24 | port 25 | ( 26 | clkin : in std_logic; 27 | clk_25 : out std_logic; 28 | locked : out std_logic 29 | ); 30 | end component; 31 | 32 | begin 33 | clk_sys_v_inst: clk_sys 34 | port map 35 | ( 36 | clkin => clkin, 37 | clk_25 => clk_25, 38 | locked => locked 39 | ); 40 | end syn; 41 | -------------------------------------------------------------------------------- /rtl_emard/lattice/top/ffmlfe5_apple2.vhd: -------------------------------------------------------------------------------- 1 | ---------------------------- 2 | -- FFM LFE5 Top level for apple2fpga 3 | -- http://github.com/emard 4 | ---------------------------- 5 | 6 | library IEEE; 7 | use IEEE.std_logic_1164.all; 8 | use IEEE.std_logic_unsigned.ALL; 9 | use IEEE.numeric_std.all; 10 | -- use IEEE.MATH_REAL.ALL; 11 | 12 | library ecp5u; 13 | use ecp5u.components.all; 14 | 15 | entity ffmlfe5_apple2 is 16 | port 17 | ( 18 | clk_100mhz_p, clk_100mhz_n: in std_logic; 19 | -- RS232 20 | --uart3_txd: out std_logic; -- rs232 txd 21 | --uart3_rxd: in std_logic; -- rs232 rxd 22 | -- SD card (SPI) 23 | sd_f_clk, sd_f_cmd: out std_logic; 24 | sd_f_d: inout std_logic_vector(3 downto 0); 25 | sd_f_cdet: in std_logic; 26 | -- SDRAM 27 | -- dr_clk: out std_logic; 28 | -- dr_cke: out std_logic; 29 | -- dr_cs_n: out std_logic; 30 | -- dr_a: out std_logic_vector(12 downto 0); 31 | -- dr_ba: out std_logic_vector(1 downto 0); 32 | -- dr_ras_n, dr_cas_n: out std_logic; 33 | -- dr_dqm: out std_logic_vector(3 downto 0); 34 | -- dr_d: inout std_logic_vector(31 downto 0); 35 | -- dr_we_n: out std_logic; 36 | -- FFM Module IO 37 | fioa: inout std_logic_vector(7 downto 0); 38 | fiob: inout std_logic_vector(31 downto 20); 39 | -- Low-Cost HDMI video out 40 | vid_d_p, vid_d_n: out std_logic_vector(3 downto 0); 41 | -- ADV7513 video chip 42 | dv_clk: inout std_logic; 43 | dv_sda: inout std_logic; 44 | dv_scl: inout std_logic; 45 | dv_int: inout std_logic; 46 | dv_de: inout std_logic; 47 | dv_hsync: inout std_logic; 48 | dv_vsync: inout std_logic; 49 | dv_spdif: inout std_logic; 50 | dv_mclk: inout std_logic; 51 | dv_i2s: inout std_logic_vector(3 downto 0); 52 | dv_sclk: inout std_logic; 53 | dv_lrclk: inout std_logic; 54 | dv_d: inout std_logic_vector(23 downto 0) 55 | ); 56 | end; 57 | 58 | architecture struct of ffmlfe5_apple2 is 59 | -- keyboard 60 | alias ps2a_clk : std_logic is fioa(6); 61 | alias ps2a_data : std_logic is fioa(4); 62 | -- mouse 63 | alias ps2b_clk : std_logic is fioa(3); 64 | alias ps2b_data : std_logic is fioa(1); 65 | 66 | alias audio_l: std_logic is fioa(2); 67 | alias audio_r: std_logic is fioa(0); 68 | 69 | alias ps2led_green: std_logic is fioa(5); -- green LED 70 | alias ps2led_red: std_logic is fioa(7); -- red LED 71 | 72 | alias sd_d: std_logic_vector(3 downto 0) is sd_f_d; 73 | alias sd_clk: std_logic is sd_f_clk; 74 | alias sd_cmd: std_logic is sd_f_cmd; 75 | 76 | alias n_joy1_up : std_logic is fiob(20); -- up 77 | alias n_joy1_down : std_logic is fiob(21); -- down 78 | alias n_joy1_left : std_logic is fiob(22); -- left 79 | alias n_joy1_right : std_logic is fiob(23); -- right 80 | alias n_joy1_fire1 : std_logic is fiob(24); -- fire1 81 | alias n_joy1_fire2 : std_logic is fiob(25); -- fire2 82 | 83 | alias n_joy2_up : std_logic is fiob(26); -- up 84 | alias n_joy2_down : std_logic is fiob(27); -- down 85 | alias n_joy2_left : std_logic is fiob(28); -- left 86 | alias n_joy2_right : std_logic is fiob(29); -- right 87 | alias n_joy2_fire1 : std_logic is fiob(30); -- fire1 88 | alias n_joy2_fire2 : std_logic is fiob(31); -- fire2 89 | 90 | alias gpdi_dp: std_logic_vector(3 downto 0) is vid_d_p; 91 | alias gpdi_dn: std_logic_vector(3 downto 0) is vid_d_n; 92 | 93 | signal clk_25MHz: std_logic; 94 | 95 | begin 96 | clksys : entity work.clk_sys_vhdl 97 | port map 98 | ( 99 | clkin => clk_100MHz_p, 100 | clk_25 => clk_25MHz, 101 | locked => open 102 | ); 103 | 104 | E_apple2: entity work.ulx3s_apple2 105 | port map 106 | ( 107 | clk_25mhz => clk_25MHz, 108 | 109 | ftdi_txd => '1', 110 | wifi_txd => '1', 111 | 112 | sw => "1111", 113 | btn => "0000001", -- btn(0) is reset_n 114 | 115 | led(0) => ps2led_red, -- floppy "IN USE>" LED 116 | 117 | audio_l(0) => audio_l, 118 | audio_r(0) => audio_r, 119 | 120 | -- PS/2 keyboard 121 | usb_fpga_bd_dp => ps2a_clk, 122 | usb_fpga_bd_dn => ps2a_data, 123 | 124 | gpdi_dp => gpdi_dp, 125 | gpdi_dn => gpdi_dn, 126 | 127 | sd_d => sd_d, 128 | sd_clk => sd_clk, 129 | sd_cmd => sd_cmd 130 | ); 131 | end struct; 132 | -------------------------------------------------------------------------------- /rtl_emard/oled/oled_font_pack.vhd: -------------------------------------------------------------------------------- 1 | -- (c) EMARD 2 | -- License=BSD 3 | 4 | library ieee; 5 | use ieee.std_logic_1164.all; 6 | 7 | -- OLED FONT 8 | 9 | package oled_font_pack is 10 | type T_oled_char is array (0 to 7) of std_logic_vector(4 downto 0); 11 | type T_oled_font is array (0 to 16) of T_oled_char; 12 | constant C_oled_font: T_oled_font := 13 | ( 14 | ( -- 0 15 | "01110", 16 | "10001", 17 | "10011", 18 | "10101", 19 | "11001", 20 | "10001", 21 | "01110", 22 | "00000" 23 | ), 24 | ( -- 1 25 | "00100", 26 | "01100", 27 | "00100", 28 | "00100", 29 | "00100", 30 | "00100", 31 | "01110", 32 | "00000" 33 | ), 34 | ( -- 2 35 | "01110", 36 | "10001", 37 | "00001", 38 | "00010", 39 | "00100", 40 | "01000", 41 | "11111", 42 | "00000" 43 | ), 44 | ( -- 3 45 | "01110", 46 | "10001", 47 | "00001", 48 | "01110", 49 | "00001", 50 | "10001", 51 | "01110", 52 | "00000" 53 | ), 54 | ( -- 4 55 | "00010", 56 | "00110", 57 | "01010", 58 | "10010", 59 | "11111", 60 | "00010", 61 | "00010", 62 | "00000" 63 | ), 64 | ( -- 5 65 | "11111", 66 | "10000", 67 | "11110", 68 | "00001", 69 | "00001", 70 | "10001", 71 | "01110", 72 | "00000" 73 | ), 74 | ( -- 6 75 | "01111", 76 | "10000", 77 | "11110", 78 | "10001", 79 | "10001", 80 | "10001", 81 | "01110", 82 | "00000" 83 | ), 84 | ( -- 7 85 | "11111", 86 | "10001", 87 | "00001", 88 | "00010", 89 | "00100", 90 | "01000", 91 | "01000", 92 | "00000" 93 | ), 94 | ( -- 8 95 | "01110", 96 | "10001", 97 | "10001", 98 | "01110", 99 | "10001", 100 | "10001", 101 | "01110", 102 | "00000" 103 | ), 104 | ( -- 9 105 | "01110", 106 | "10001", 107 | "10001", 108 | "01111", 109 | "00001", 110 | "10001", 111 | "01110", 112 | "00000" 113 | ), 114 | ( -- 10: A 115 | "01110", 116 | "10001", 117 | "10001", 118 | "11111", 119 | "10001", 120 | "10001", 121 | "10001", 122 | "00000" 123 | ), 124 | ( -- 11: B 125 | "11110", 126 | "10001", 127 | "10001", 128 | "11110", 129 | "10001", 130 | "10001", 131 | "11110", 132 | "00000" 133 | ), 134 | ( -- 12: C 135 | "01110", 136 | "10001", 137 | "10000", 138 | "10000", 139 | "10000", 140 | "10001", 141 | "01110", 142 | "00000" 143 | ), 144 | ( -- 13: D 145 | "11110", 146 | "10001", 147 | "10001", 148 | "10001", 149 | "10001", 150 | "10001", 151 | "11110", 152 | "00000" 153 | ), 154 | ( -- 14: E 155 | "11111", 156 | "10000", 157 | "10000", 158 | "11110", 159 | "10000", 160 | "10000", 161 | "11111", 162 | "00000" 163 | ), 164 | ( -- 15: F 165 | "11111", 166 | "10000", 167 | "10000", 168 | "11110", 169 | "10000", 170 | "10000", 171 | "10000", 172 | "00000" 173 | ), 174 | ( -- 16: SPACE 175 | "00000", 176 | "00000", 177 | "00000", 178 | "00000", 179 | "00000", 180 | "00000", 181 | "00000", 182 | "00000" 183 | ) 184 | ); 185 | end; 186 | -------------------------------------------------------------------------------- /rtl_emard/oled/oled_init_pack.vhd: -------------------------------------------------------------------------------- 1 | -- (c) EMARD 2 | -- License=BSD 3 | 4 | library ieee; 5 | use ieee.std_logic_1164.all; 6 | 7 | -- OLED initialization sequence 8 | -- next byte after a NOP command encodes delay in ms 9 | 10 | package oled_init_pack is 11 | -- all this are commands and should be send with DC line low 12 | constant C_OLED_NOP1: std_logic_vector(7 downto 0) := x"BC"; -- 10111100 13 | constant C_OLED_NOP2: std_logic_vector(7 downto 0) := x"BD"; -- delay nop 14 | constant C_OLED_NOP3: std_logic_vector(7 downto 0) := x"E3"; 15 | constant C_OLED_SET_DISPLAY_OFF: std_logic_vector(7 downto 0) := x"AE"; -- 10101110 16 | constant C_OLED_SET_REMAP_COLOR: std_logic_vector(7 downto 0) := x"A0"; 17 | constant C_OLED_ULX3S_REMAP: std_logic_vector(7 downto 0) := "00100010"; -- A[7:6] = 00; 256 color. A[7:6] = 01; 65k color format rotation for ULX3S, A[1] = 1 scan right to left 18 | constant C_OLED_SET_DISPLAY_START_LINE: std_logic_vector(7 downto 0) := x"A1"; 19 | constant C_OLED_SET_DISPLAY_OFFSET: std_logic_vector(7 downto 0) := x"A1"; 20 | constant C_OLED_SET_DISPLAY_MODE_NORMAL: std_logic_vector(7 downto 0) := x"A4"; 21 | constant C_OLED_SET_MULTIPLEX_RATIO: std_logic_vector(7 downto 0) := x"A8"; 22 | constant C_OLED_SET_MASTER_CONFIGURATION: std_logic_vector(7 downto 0) := x"AD"; 23 | constant C_OLED_SET_POWER_SAVE_MODE: std_logic_vector(7 downto 0) := x"B0"; 24 | constant C_OLED_SET_PHASE_1_AND_2_PERIOD_ADJUSTMENT: std_logic_vector(7 downto 0) := x"B1"; 25 | constant C_OLED_SET_DISPLAY_CLOCK_DIVIDER: std_logic_vector(7 downto 0) := x"F0"; 26 | constant C_OLED_SET_PRECHARGE_A: std_logic_vector(7 downto 0) := x"8A"; 27 | constant C_OLED_SET_PRECHARGE_B: std_logic_vector(7 downto 0) := x"8B"; 28 | constant C_OLED_SET_PRECHARGE_C: std_logic_vector(7 downto 0) := x"8C"; 29 | constant C_OLED_SET_PRECHARGE_LEVEL: std_logic_vector(7 downto 0) := x"BB"; 30 | constant C_OLED_SET_VCOMH: std_logic_vector(7 downto 0) := x"BE"; 31 | constant C_OLED_SET_MASTER_CURRENT_CONTROL: std_logic_vector(7 downto 0) := x"87"; 32 | constant C_OLED_SET_CONTRAST_COLOR_A: std_logic_vector(7 downto 0) := x"81"; 33 | constant C_OLED_SET_CONTRAST_COLOR_B: std_logic_vector(7 downto 0) := x"82"; 34 | constant C_OLED_SET_CONTRAST_COLOR_C: std_logic_vector(7 downto 0) := x"83"; 35 | constant C_OLED_SET_COLUMN_ADDRESS: std_logic_vector(7 downto 0) := x"15"; 36 | constant C_OLED_SET_ROW_ADDRESS: std_logic_vector(7 downto 0) := x"75"; 37 | constant C_OLED_SET_DISPLAY_ON: std_logic_vector(7 downto 0) := x"AF"; 38 | 39 | type T_oled_init_seq is array (0 to 44) of std_logic_vector(7 downto 0); 40 | constant C_oled_init_seq: T_oled_init_seq := 41 | ( 42 | C_OLED_NOP1, -- 0, 10111100 43 | C_OLED_SET_DISPLAY_OFF, -- 1, 10101110 44 | C_OLED_SET_REMAP_COLOR, C_OLED_ULX3S_REMAP, -- 2 45 | C_OLED_SET_DISPLAY_START_LINE, x"00", -- 4 46 | C_OLED_SET_DISPLAY_OFFSET, x"00", -- 6 47 | C_OLED_SET_DISPLAY_MODE_NORMAL, -- 8 48 | C_OLED_SET_MULTIPLEX_RATIO, "00111111", -- 9, 15-16 49 | C_OLED_SET_MASTER_CONFIGURATION, "10001110", -- 11, a[0]=0 Select external Vcc supply, a[0]=1 Reserved(reset) 50 | C_OLED_SET_POWER_SAVE_MODE, x"00", -- 13, 0-no power save, x"1A"-power save 51 | C_OLED_SET_PHASE_1_AND_2_PERIOD_ADJUSTMENT, x"74", -- 15 52 | C_OLED_SET_DISPLAY_CLOCK_DIVIDER, x"F0", -- 17 53 | C_OLED_SET_PRECHARGE_A, x"64", -- 19, 100 54 | C_OLED_SET_PRECHARGE_B, x"78", -- 21, 120 55 | C_OLED_SET_PRECHARGE_C, x"64", -- 23, 100 56 | C_OLED_SET_PRECHARGE_LEVEL, x"31", -- 25, 49 57 | C_OLED_SET_CONTRAST_COLOR_A, x"FF", -- 27, 255 58 | C_OLED_SET_CONTRAST_COLOR_B, x"FF", -- 29, 255 59 | C_OLED_SET_CONTRAST_COLOR_C, x"FF", -- 31, 255 60 | C_OLED_SET_VCOMH, x"3E", -- 33, 62 61 | C_OLED_SET_MASTER_CURRENT_CONTROL, x"06", -- 35, 6 62 | C_OLED_SET_COLUMN_ADDRESS, x"00", x"5F", -- 37, 96 63 | C_OLED_SET_ROW_ADDRESS, x"00", x"3F", -- 40, 63 64 | C_OLED_SET_DISPLAY_ON, -- 43 65 | C_OLED_NOP1 -- 44 -- during debugging sent as data, counter relodaded in oled.vhd 66 | ); 67 | end; 68 | -------------------------------------------------------------------------------- /rtl_emard/osd/font2readmemb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # convert binary file (usually 2K or 4K to verilog $readmemb() 4 | import sys 5 | 6 | font_height=16 7 | msb_first=1 8 | 9 | def printer(n, b): 10 | if n >= 32 and n <= 127: 11 | print("// 0x%02X %c" % (n, n)) 12 | else: 13 | print("// 0x%02X" % n) 14 | if msb_first: 15 | for c in b: 16 | for i in range(8): 17 | print((c&0x80)>>7, end="") 18 | c <<= 1 19 | print("") 20 | else: # lsb first 21 | for c in b: 22 | for i in range(8): 23 | print(c&1, end="") 24 | c >>= 1 25 | print("") 26 | 27 | def converter(): 28 | f=sys.stdin 29 | print('// spi_osd.v: initial $readmemb("font_bizcat8x16.mem", font);') 30 | b = bytearray(font_height) 31 | i = 0 32 | while True: 33 | b = f.buffer.read(16) 34 | if b: 35 | printer(i,b) 36 | i += 1 37 | else: 38 | break 39 | f.close() 40 | 41 | converter() 42 | -------------------------------------------------------------------------------- /rtl_emard/osd/font_bizcat8x16.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emard/apple2fpga/0ad84ae2f9d9d5cc0e8db6128d630a9e16cdd6de/rtl_emard/osd/font_bizcat8x16.raw -------------------------------------------------------------------------------- /rtl_emard/osd/osd.v: -------------------------------------------------------------------------------- 1 | // intercept video stream and make a window 2 | 3 | module osd 4 | #( 5 | parameter c_x_start = 128, 6 | parameter c_x_stop = 383, 7 | parameter c_y_start = 128, 8 | parameter c_y_stop = 383, 9 | parameter c_x_bits = 10, 10 | parameter c_y_bits = 10, 11 | parameter c_transparency = 0 12 | ) 13 | ( 14 | input wire clk_pixel, clk_pixel_ena, 15 | input wire [7:0] i_r, i_g, i_b, 16 | input wire i_hsync, i_vsync, i_blank, 17 | input wire i_osd_en, 18 | input wire [7:0] i_osd_r, i_osd_g, i_osd_b, 19 | output wire [c_x_bits-1:0] o_osd_x, 20 | output wire [c_y_bits-1:0] o_osd_y, 21 | output wire [7:0] o_r, o_g, o_b, 22 | output wire o_hsync, o_vsync, o_blank, 23 | output wire o_osd_en 24 | ); 25 | 26 | reg osd_en, osd_xen, osd_yen; 27 | reg R_xcount_en, R_ycount_en; 28 | reg R_hsync_prev; 29 | reg [c_x_bits-1:0] R_xcount; 30 | reg [c_y_bits-1:0] R_ycount; // relative to screen 31 | reg [c_x_bits-1:0] R_osd_x; 32 | reg [c_y_bits-1:0] R_osd_y; // relative to OSD 33 | always @(posedge clk_pixel) 34 | begin 35 | if(clk_pixel_ena) 36 | begin 37 | if(i_vsync) 38 | begin 39 | R_ycount <= 0; 40 | R_ycount_en <= 0; // wait for blank before counting 41 | end 42 | else 43 | begin 44 | if(i_blank == 1'b0) // display unblanked 45 | R_ycount_en <= 1'b1; 46 | if(R_hsync_prev == 1'b0 && i_hsync == 1'b1) 47 | begin // hsync rising edge 48 | R_xcount <= 0; 49 | R_xcount_en <= 0; 50 | if(R_ycount_en) 51 | R_ycount <= R_ycount + 1; 52 | if(R_ycount == c_y_start) 53 | begin 54 | osd_yen <= 1; 55 | R_osd_y <= 0; 56 | end 57 | if(osd_yen) 58 | R_osd_y <= R_osd_y + 1; 59 | if(R_ycount == c_y_stop) 60 | begin 61 | osd_yen <= 0; 62 | end 63 | end 64 | else 65 | begin 66 | if(i_blank == 1'b0) // display unblanked 67 | R_xcount_en <= 1'b1; 68 | if(R_xcount_en) 69 | R_xcount <= R_xcount + 1; 70 | if(R_xcount == c_x_start) 71 | begin 72 | osd_xen <= 1; 73 | R_osd_x <= 0; 74 | end 75 | if(osd_xen) 76 | R_osd_x <= R_osd_x + 1; 77 | if(R_xcount == c_x_stop) 78 | begin 79 | osd_xen <= 0; 80 | end 81 | end 82 | R_hsync_prev <= i_hsync; 83 | end 84 | osd_en <= osd_xen & osd_yen; 85 | end 86 | end 87 | 88 | reg [7:0] R_vga_r, R_vga_g, R_vga_b; 89 | reg R_hsync, R_vsync, R_blank; 90 | generate 91 | if(c_transparency) 92 | always @(posedge clk_pixel) 93 | begin 94 | if(clk_pixel_ena) 95 | begin 96 | if(osd_en & i_osd_en) 97 | begin 98 | R_vga_r <= {i_osd_r[7],i_osd_r[6:0]|i_r[7:1]}; 99 | R_vga_g <= {i_osd_g[7],i_osd_g[6:0]|i_g[7:1]}; 100 | R_vga_b <= {i_osd_b[7],i_osd_b[6:0]|i_b[7:1]}; 101 | end 102 | else 103 | begin 104 | R_vga_r <= i_r; 105 | R_vga_g <= i_g; 106 | R_vga_b <= i_b; 107 | end 108 | R_hsync <= i_hsync; 109 | R_vsync <= i_vsync; 110 | R_blank <= i_blank; 111 | end 112 | end 113 | else // c_transparency == 0 114 | always @(posedge clk_pixel) 115 | begin 116 | if(clk_pixel_ena) 117 | begin 118 | if(osd_en & i_osd_en) 119 | begin 120 | R_vga_r <= i_osd_r; 121 | R_vga_g <= i_osd_g; 122 | R_vga_b <= i_osd_b; 123 | end 124 | else 125 | begin 126 | R_vga_r <= i_r; 127 | R_vga_g <= i_g; 128 | R_vga_b <= i_b; 129 | end 130 | R_hsync <= i_hsync; 131 | R_vsync <= i_vsync; 132 | R_blank <= i_blank; 133 | end 134 | end 135 | endgenerate 136 | 137 | assign o_osd_x = R_osd_x; 138 | assign o_osd_y = R_osd_y; 139 | assign o_r = R_vga_r; 140 | assign o_g = R_vga_g; 141 | assign o_b = R_vga_b; 142 | assign o_hsync = R_hsync; 143 | assign o_vsync = R_vsync; 144 | assign o_blank = R_blank; 145 | assign o_osd_en = osd_en; 146 | 147 | endmodule 148 | -------------------------------------------------------------------------------- /rtl_emard/osd/osd_vhd.vhd: -------------------------------------------------------------------------------- 1 | library ieee; 2 | use ieee.std_logic_1164.all; 3 | use ieee.std_logic_unsigned.all; 4 | use ieee.std_logic_arith.all; 5 | 6 | entity osd_vhd is 7 | generic 8 | ( 9 | c_x_start : natural := 128; -- x1 pixel window h-position 10 | c_x_stop : natural := 383; -- x1 pixel window h-position 11 | c_y_start : natural := 128; -- x1 pixel window v-position 12 | c_y_stop : natural := 383; -- x1 pixel window v-position 13 | c_x_bits : natural := 10; -- bits in x counter 14 | c_y_bits : natural := 10; -- bits in y counter 15 | c_transparency : natural := 0 -- 1:see-thru OSD menu 0:opaque 16 | ); 17 | port 18 | ( 19 | clk_pixel, clk_pixel_ena : in std_logic; 20 | i_r, i_g, i_b : in std_logic_vector(7 downto 0); 21 | i_hsync, i_vsync, i_blank : in std_logic; 22 | i_osd_en : in std_logic; 23 | i_osd_r, i_osd_g, i_osd_b : in std_logic_vector(7 downto 0); 24 | o_osd_x : out std_logic_vector(c_x_bits-1 downto 0); 25 | o_osd_y : out std_logic_vector(c_y_bits-1 downto 0); 26 | o_r, o_g, o_b : out std_logic_vector(7 downto 0); 27 | o_hsync, o_vsync, o_blank : out std_logic; 28 | o_osd_en : out std_logic 29 | ); 30 | end; 31 | 32 | architecture syn of osd_vhd is 33 | component osd -- verilog name and its parameters 34 | generic 35 | ( 36 | c_x_start : natural; 37 | c_x_stop : natural; 38 | c_y_start : natural; 39 | c_y_stop : natural; 40 | c_x_bits : natural; 41 | c_y_bits : natural; 42 | c_transparency : natural 43 | ); 44 | port 45 | ( 46 | clk_pixel, clk_pixel_ena : in std_logic; 47 | i_r, i_g, i_b : in std_logic_vector(7 downto 0); 48 | i_hsync, i_vsync, i_blank : in std_logic; 49 | i_osd_en : in std_logic; 50 | i_osd_r, i_osd_g, i_osd_b : in std_logic_vector(7 downto 0); 51 | o_osd_x : out std_logic_vector(c_x_bits-1 downto 0); 52 | o_osd_y : out std_logic_vector(c_y_bits-1 downto 0); 53 | o_r, o_g, o_b : out std_logic_vector(7 downto 0); 54 | o_hsync, o_vsync, o_blank : out std_logic; 55 | o_osd_en : out std_logic 56 | ); 57 | end component; 58 | 59 | begin 60 | spi_osd_v_inst: osd 61 | generic map 62 | ( 63 | c_x_start => c_x_start, 64 | c_x_stop => c_x_stop, 65 | c_y_start => c_y_start, 66 | c_y_stop => c_y_stop, 67 | c_x_bits => c_x_bits, 68 | c_y_bits => c_y_bits, 69 | c_transparency => c_transparency 70 | ) 71 | port map 72 | ( 73 | clk_pixel => clk_pixel, clk_pixel_ena => clk_pixel_ena, 74 | i_r => i_r, i_g => i_g, i_b => i_b, 75 | i_hsync => i_hsync, i_vsync => i_vsync, i_blank => i_blank, 76 | i_osd_en => i_osd_en, 77 | i_osd_r => i_osd_r, i_osd_g => i_osd_g, i_osd_b => i_osd_b, 78 | o_osd_x => o_osd_x, 79 | o_osd_y => o_osd_y, 80 | o_r => o_r, o_g => o_g, o_b => o_b, 81 | o_hsync => o_hsync, o_vsync => o_vsync, o_blank => o_blank, 82 | o_osd_en => o_osd_en 83 | ); 84 | end syn; 85 | -------------------------------------------------------------------------------- /rtl_emard/osd/spi_osd.vhd: -------------------------------------------------------------------------------- 1 | library ieee; 2 | use ieee.std_logic_1164.all; 3 | use ieee.std_logic_unsigned.all; 4 | use ieee.std_logic_arith.all; 5 | 6 | entity spi_osd is 7 | generic 8 | ( 9 | c_addr_enable : std_logic_vector(7 downto 0) := x"FE"; -- high addr byte of enable byte 10 | c_addr_display : std_logic_vector(7 downto 0) := x"FD"; -- high addr byte of display data, +0x10000 for inverted 11 | c_start_x : natural := 64; -- x1 pixel window h-position 12 | c_start_y : natural := 48; -- x1 pixel window v-position 13 | c_char_bits_x : natural := 5; -- 2**n chars h-size (x8 pixels) 14 | c_chars_y : natural := 24; -- chars v-size (x16 pixels) 15 | c_bits_x : natural := 10; -- bits in x counter 16 | c_bits_y : natural := 10; -- bits in y counter 17 | c_init_on : natural := 1; -- 0:default OFF 1:default ON 18 | c_inverse : natural := 1; -- 0:no inverse, 1:inverse chars support 19 | c_transparency : natural := 0; -- 1:see-thru OSD menu 0:opaque 20 | c_bgcolor : std_logic_vector(23 downto 0) := x"503020"; -- RRGGBB menu background color 21 | c_char_file : string := "osd.mem"; -- initial window content, 2 ASCII HEX digits per line 22 | c_font_file : string := "font_bizcat8x16.mem" -- font bitmap, 8 ASCII BIN digits per line 23 | ); 24 | port 25 | ( 26 | clk_pixel, clk_pixel_ena : in std_logic; 27 | i_r, i_g, i_b : in std_logic_vector(7 downto 0); 28 | i_hsync, i_vsync, i_blank: in std_logic; 29 | i_csn, i_sclk, i_mosi : in std_logic; 30 | --o_miso : inout std_logic; 31 | o_r, o_g, o_b : out std_logic_vector(7 downto 0); 32 | o_hsync, o_vsync, o_blank: out std_logic 33 | ); 34 | end; 35 | 36 | architecture syn of spi_osd is 37 | component spi_osd_v -- verilog name and its parameters 38 | generic 39 | ( 40 | c_addr_enable : std_logic_vector(7 downto 0); 41 | c_addr_display : std_logic_vector(7 downto 0); 42 | c_start_x : natural; 43 | c_start_y : natural; 44 | c_char_bits_x : natural; 45 | c_chars_y : natural; 46 | c_bits_x : natural; 47 | c_bits_y : natural; 48 | c_init_on : natural; 49 | c_inverse : natural; 50 | c_transparency : natural; 51 | c_bgcolor : std_logic_vector(23 downto 0); 52 | c_char_file : string; 53 | c_font_file : string 54 | ); 55 | port 56 | ( 57 | clk_pixel, clk_pixel_ena : in std_logic; 58 | i_r, i_g, i_b : in std_logic_vector(7 downto 0); 59 | i_hsync, i_vsync, i_blank: in std_logic; 60 | i_csn, i_sclk, i_mosi : in std_logic; 61 | --o_miso : inout std_logic; 62 | o_r, o_g, o_b : out std_logic_vector(7 downto 0); 63 | o_hsync, o_vsync, o_blank: out std_logic 64 | ); 65 | end component; 66 | 67 | begin 68 | spi_osd_v_inst: spi_osd_v 69 | generic map 70 | ( 71 | c_addr_enable => c_addr_enable, 72 | c_addr_display => c_addr_display, 73 | c_start_x => c_start_x, 74 | c_start_y => c_start_y, 75 | c_char_bits_x => c_char_bits_x, 76 | c_chars_y => c_chars_y, 77 | c_bits_x => c_bits_x, 78 | c_bits_y => c_bits_y, 79 | c_init_on => c_init_on, 80 | c_inverse => c_inverse, 81 | c_transparency => c_transparency, 82 | c_bgcolor => c_bgcolor, 83 | c_char_file => c_char_file, 84 | c_font_file => c_font_file 85 | ) 86 | port map 87 | ( 88 | clk_pixel => clk_pixel, clk_pixel_ena => clk_pixel_ena, 89 | i_r => i_r, i_g => i_g, i_b => i_b, 90 | i_hsync => i_hsync, i_vsync => i_vsync, i_blank => i_blank, 91 | i_csn => i_csn, i_sclk => i_sclk, i_mosi => i_mosi, 92 | -- o_miso => o_miso, 93 | o_r => o_r, o_g => o_g, o_b => o_b, 94 | o_hsync => o_hsync, o_vsync => o_vsync, o_blank => o_blank 95 | ); 96 | end syn; 97 | -------------------------------------------------------------------------------- /rtl_emard/osd/spi_osd_v.v: -------------------------------------------------------------------------------- 1 | // SPI receiver for OSD text window 2 | // VGA video stream pipeline processor 3 | 4 | module spi_osd_v 5 | #( 6 | parameter [7:0] c_addr_enable = 8'hFE, // high addr byte of enable byte 7 | parameter [7:0] c_addr_display = 8'hFD, // high addr byte of display data, +0x10000 for inverted 8 | parameter c_start_x = 64, // x1 pixel window h-position 9 | parameter c_start_y = 48, // x1 pixel window v-position 10 | parameter c_char_bits_x = 6, // chars H-size 2**n (x8 pixels) 11 | parameter c_chars_y = 20, // chars V-size (x16 pixels) 12 | parameter c_bits_x = 10, // bits for X counter 13 | parameter c_bits_y = 10, // bits for Y counter 14 | parameter c_init_on = 1, // 0:default OFF 1:default ON 15 | parameter c_inverse = 1, // 0:no inverse, 1:inverse support 16 | parameter c_transparency = 1, // 1:see-thru OSD menu 0:opaque 17 | parameter [23:0] c_bgcolor = 24'h503020, // RRGGBB menu background color 18 | parameter c_char_file = "osd.mem", // initial window content, 2 ASCII HEX digits per line 19 | parameter c_font_file = "font_bizcat8x16.mem" // font bitmap, 8 ASCII BIN digits per line 20 | ) 21 | ( 22 | input wire clk_pixel, clk_pixel_ena, 23 | input wire [7:0] i_r, 24 | input wire [7:0] i_g, 25 | input wire [7:0] i_b, 26 | input wire i_hsync, i_vsync, i_blank, 27 | input wire i_csn, i_sclk, i_mosi, 28 | //inout wire o_miso, // NOTE it would work but disabled to simplify core 29 | output wire [7:0] o_r, 30 | output wire [7:0] o_g, 31 | output wire [7:0] o_b, 32 | output wire o_hsync, o_vsync, o_blank 33 | ); 34 | localparam c_chars_x = 2**c_char_bits_x; 35 | reg [7+c_inverse:0] tile_map [0:c_chars_x*c_chars_y-1]; // tile memory (character map) 36 | initial 37 | $readmemh(c_char_file, tile_map); 38 | 39 | wire ram_wr; 40 | wire [31:0] ram_addr; 41 | wire [7:0] ram_di; 42 | reg [7:0] ram_do; 43 | spirw_slave_v 44 | #( 45 | .c_addr_bits(32), 46 | .c_sclk_capable_pin(1'b0) 47 | ) 48 | spirw_slave_inst 49 | ( 50 | .clk(clk_pixel), 51 | .csn(i_csn), 52 | .sclk(i_sclk), 53 | .mosi(i_mosi), 54 | //.miso(o_miso), // NOTE it would work but disabled to simplify core 55 | .wr(ram_wr), 56 | .addr(ram_addr), 57 | .data_in(ram_do), 58 | .data_out(ram_di) 59 | ); 60 | always @(posedge clk_pixel) 61 | begin 62 | if(ram_wr && (ram_addr[31:24] == c_addr_display)) 63 | if(c_inverse) 64 | tile_map[ram_addr] <= {ram_addr[16],ram_di}; // ASCII to 0xFDx0xxxx normal, 0xFDx1xxxx inverted 65 | else 66 | tile_map[ram_addr] <= ram_di; 67 | //ram_do <= tile_map[ram_addr]; 68 | end 69 | 70 | reg osd_en = c_init_on; 71 | always @(posedge clk_pixel) 72 | begin 73 | if(ram_wr && (ram_addr[31:24] == c_addr_enable)) // write to 0xFExxxxxx enables/disables OSD 74 | osd_en <= ram_di[0]; 75 | end 76 | 77 | wire [c_bits_x-1:0] osd_x; 78 | wire [c_bits_y-1:0] osd_y; 79 | reg [7:0] font[0:4095]; 80 | initial 81 | $readmemb(c_font_file, font); 82 | reg [7:0] data_out; 83 | // previous design was: 84 | // wire [11:0] tileaddr = osd_y[c_bits_y-1:4] * c_chars_x + osd_x[c_bits_x-1:3]; 85 | // limited to 2**n x-size, avoids arithmetic: 86 | wire [11:0] tileaddr = {osd_y[c_bits_y-1:4], osd_x[c_char_bits_x+2:3]}; 87 | generate 88 | if(c_inverse) 89 | always @(posedge clk_pixel) 90 | data_out[7:0] <= font[{tile_map[tileaddr], osd_y[3:0]}] ^ {8{tile_map[tileaddr][8]}}; 91 | else 92 | always @(posedge clk_pixel) 93 | data_out[7:0] <= font[{tile_map[tileaddr], osd_y[3:0]}]; 94 | endgenerate 95 | wire [7:0] data_out_align = {data_out[0], data_out[7:1]}; 96 | wire osd_pixel = data_out_align[7-osd_x[2:0]]; 97 | 98 | wire [7:0] osd_r = osd_pixel ? 8'hff : c_bgcolor[23:16]; 99 | wire [7:0] osd_g = osd_pixel ? 8'hff : c_bgcolor[15:8]; 100 | wire [7:0] osd_b = osd_pixel ? 8'hff : c_bgcolor[7:0]; 101 | 102 | // OSD overlay 103 | osd 104 | #( 105 | .c_x_start(c_start_x), 106 | .c_x_stop (c_start_x+8*c_chars_x-1), 107 | .c_y_start(c_start_y), 108 | .c_x_bits(c_bits_x), 109 | .c_y_bits(c_bits_y), 110 | .c_y_stop (c_start_y+16*c_chars_y-1), 111 | .c_transparency(c_transparency) 112 | ) 113 | osd_instance 114 | ( 115 | .clk_pixel(clk_pixel), 116 | .clk_pixel_ena(clk_pixel_ena), 117 | .i_r(i_r), 118 | .i_g(i_g), 119 | .i_b(i_b), 120 | .i_hsync(i_hsync), 121 | .i_vsync(i_vsync), 122 | .i_blank(i_blank), 123 | .i_osd_en(osd_en), 124 | .o_osd_x(osd_x), 125 | .o_osd_y(osd_y), 126 | .i_osd_r(osd_r), 127 | .i_osd_g(osd_g), 128 | .i_osd_b(osd_b), 129 | .o_r(o_r), 130 | .o_g(o_g), 131 | .o_b(o_b), 132 | .o_hsync(o_hsync), 133 | .o_vsync(o_vsync), 134 | .o_blank(o_blank) 135 | ); 136 | 137 | endmodule 138 | -------------------------------------------------------------------------------- /rtl_emard/osd/spi_ram_btn.vhd: -------------------------------------------------------------------------------- 1 | -- VHDL wrapper for spi_ram_btn_v.v 2 | 3 | library ieee; 4 | use ieee.std_logic_1164.all; 5 | use ieee.std_logic_unsigned.all; 6 | use ieee.std_logic_arith.all; 7 | 8 | entity spi_ram_btn is 9 | generic 10 | ( 11 | c_addr_floppy : std_logic_vector(7 downto 0) := x"D0"; -- high addr byte of floppy req type 12 | c_addr_btn : std_logic_vector(7 downto 0) := x"FB"; -- addr byte of BTNs 13 | c_addr_irq : std_logic_vector(7 downto 0) := x"F1"; -- high addr byte of IRQ flag 14 | c_debounce_bits : natural := 20; -- more -> slower BTNs 15 | c_addr_bits : natural := 32; -- don't touch 16 | c_sclk_capable_pin : natural := 0 -- 0-sclk is generic pin, 1-sclk is clock capable pin 17 | ); 18 | port 19 | ( 20 | clk : in std_logic; 21 | csn, sclk, mosi : in std_logic; 22 | miso : inout std_logic; 23 | btn : in std_logic_vector(6 downto 0); 24 | irq : out std_logic; 25 | floppy_req_type : in std_logic_vector(7 downto 0) := (others => '0'); 26 | floppy_req : in std_logic := '0'; 27 | floppy_in_drive : in std_logic_vector(1 downto 0) := (others => '0'); -- future expansion 28 | rd, wr : out std_logic; 29 | addr : out std_logic_vector(c_addr_bits-1 downto 0); 30 | data_in : in std_logic_vector(7 downto 0); 31 | data_out : out std_logic_vector(7 downto 0) 32 | ); 33 | end; 34 | 35 | architecture syn of spi_ram_btn is 36 | component spi_ram_btn_v -- verilog name and its parameters 37 | generic 38 | ( 39 | c_addr_floppy : std_logic_vector(7 downto 0); 40 | c_addr_btn : std_logic_vector(7 downto 0); 41 | c_addr_irq : std_logic_vector(7 downto 0); 42 | c_debounce_bits : natural; 43 | c_addr_bits : natural; 44 | c_sclk_capable_pin : natural 45 | ); 46 | port 47 | ( 48 | clk : in std_logic; 49 | csn, sclk, mosi : in std_logic; 50 | miso : inout std_logic; 51 | btn : in std_logic_vector(6 downto 0); 52 | irq : out std_logic; 53 | floppy_req_type : in std_logic_vector(7 downto 0); 54 | floppy_req : in std_logic; 55 | floppy_in_drive : in std_logic_vector(1 downto 0); 56 | rd, wr : out std_logic; 57 | addr : out std_logic_vector(c_addr_bits-1 downto 0); 58 | data_in : in std_logic_vector(7 downto 0); 59 | data_out : out std_logic_vector(7 downto 0) 60 | ); 61 | end component; 62 | 63 | begin 64 | spi_ram_btn_v_inst: spi_ram_btn_v 65 | generic map 66 | ( 67 | c_addr_floppy => c_addr_floppy, 68 | c_addr_btn => c_addr_btn, 69 | c_addr_irq => c_addr_irq, 70 | c_debounce_bits => c_debounce_bits, 71 | c_addr_bits => c_addr_bits, 72 | c_sclk_capable_pin => c_sclk_capable_pin 73 | ) 74 | port map 75 | ( 76 | clk => clk, 77 | csn => csn, sclk => sclk, mosi => mosi, 78 | miso => miso, 79 | btn => btn, 80 | irq => irq, 81 | floppy_req_type => floppy_req_type, 82 | floppy_req => floppy_req, 83 | floppy_in_drive => floppy_in_drive, 84 | rd => rd, wr => wr, 85 | addr => addr, 86 | data_in => data_in, 87 | data_out => data_out 88 | ); 89 | end syn; 90 | -------------------------------------------------------------------------------- /rtl_emard/osd/spi_ram_btn_v.v: -------------------------------------------------------------------------------- 1 | // SPI RAM RW slave with BTN IRQ 2 | 3 | // AUTHOR=EMARD 4 | // LICENSE=BSD 5 | 6 | // 0xFBxxxxxx: {irq,btn[6:0]} 7 | // others : SPI RAM 8 | 9 | // read with dummy byte which should be discarded 10 | 11 | // write 00 12 | // read 01 13 | 14 | module spi_ram_btn_v 15 | #( 16 | parameter [7:0] c_addr_floppy = 8'hD0, // high addr byte of floppy req type 17 | parameter [7:0] c_addr_btn = 8'hFB, // high addr byte of BTNs 18 | parameter [7:0] c_addr_irq = 8'hF1, // high addr byte of IRQ flag 19 | parameter c_debounce_bits = 20, // more -> slower BTNs 20 | parameter c_addr_bits = 32, // don't touch 21 | parameter c_sclk_capable_pin = 0 //, // 0-sclk is generic pin, 1-sclk is clock capable pin 22 | ) 23 | ( 24 | input wire clk, // faster than SPI clock 25 | input wire csn, sclk, mosi, // SPI lines to be sniffed 26 | inout wire miso, // 3-state line, active when csn=0 27 | // BTNs 28 | input wire [6:0] btn, 29 | output wire irq, // to ESP32 30 | // floppies 31 | input wire [7:0] floppy_req_type, // floppy number and track 32 | input wire floppy_req, // sets irq (set floppy_req_type and pulse this high for min 1 clock) 33 | input wire [1:0] floppy_in_drive, 34 | // BRAM interface 35 | output wire rd, wr, 36 | output wire [c_addr_bits-1:0] addr, 37 | input wire [7:0] data_in, 38 | output wire [7:0] data_out 39 | ); 40 | 41 | // IRQ controller tracks BTN state 42 | reg R_btn_irq; // BTN IRQ flag 43 | reg R_floppy_irq; // floppy IRQ flag 44 | reg R_floppy_req; // external request 45 | reg [7:0] R_floppy_req_type; 46 | reg [6:0] R_btn, R_btn_latch; 47 | reg R_spi_rd; 48 | reg [c_debounce_bits-1:0] R_btn_debounce; 49 | always @(posedge clk) 50 | begin 51 | R_spi_rd <= rd; 52 | if(rd == 1'b0 && R_spi_rd == 1'b1 && addr[c_addr_bits-1:c_addr_bits-8] == c_addr_irq) 53 | begin // reading 0xF1.. resets IRQ flags 54 | R_btn_irq <= 1'b0; 55 | R_floppy_irq <= 1'b0; 56 | end 57 | else // BTN state is read from 0xFBxxxxxx 58 | begin 59 | // floppy req rising edge sets floppy IRQ flag 60 | // and stores request type in a register for later reading from SPI 61 | if(floppy_req == 1'b1 && R_floppy_req == 1'b0) 62 | begin 63 | R_floppy_req_type <= floppy_req_type; 64 | R_floppy_irq <= 1'b1; 65 | end 66 | R_floppy_req <= floppy_req; 67 | // changed BTN state sets BTN IRQ flag 68 | R_btn_latch <= btn; 69 | if(R_btn != R_btn_latch && R_btn_debounce[$bits(R_btn_debounce)-1] == 1 && R_btn_irq == 0) 70 | begin 71 | R_btn_irq <= 1'b1; 72 | R_btn_debounce <= 0; 73 | R_btn <= R_btn_latch; 74 | end 75 | else 76 | if(R_btn_debounce[$bits(R_btn_debounce)-1] == 1'b0) 77 | R_btn_debounce <= R_btn_debounce + 1; 78 | end 79 | end 80 | 81 | wire [7:0] mux_data_in = addr[c_addr_bits-1:c_addr_bits-8] == c_addr_irq ? {R_btn_irq,6'b0,R_floppy_irq} 82 | : addr[c_addr_bits-1:c_addr_bits-8] == c_addr_btn ? {1'b0,R_btn} 83 | : addr[c_addr_bits-1:c_addr_bits-8] == c_addr_floppy ? R_floppy_req_type 84 | : data_in; 85 | assign irq = R_btn_irq | R_floppy_irq; 86 | 87 | spirw_slave_v 88 | #( 89 | .c_sclk_capable_pin(c_sclk_capable_pin), 90 | .c_addr_bits(c_addr_bits) 91 | ) 92 | spirw_slave_v_inst 93 | ( 94 | .clk(clk), 95 | .csn(csn), 96 | .sclk(sclk), 97 | .mosi(mosi), 98 | .miso(miso), 99 | .wr(wr), 100 | .rd(rd), 101 | .addr(addr), 102 | .data_in(mux_data_in), 103 | .data_out(data_out) 104 | ); 105 | 106 | endmodule 107 | -------------------------------------------------------------------------------- /rtl_emard/osd/spirw_slave.vhd: -------------------------------------------------------------------------------- 1 | -- VHDL wrapper for spirw_slave_v.v 2 | 3 | library ieee; 4 | use ieee.std_logic_1164.all; 5 | use ieee.std_logic_unsigned.all; 6 | use ieee.std_logic_arith.all; 7 | 8 | entity spirw_slave is 9 | generic 10 | ( 11 | c_addr_bits : natural := 16; 12 | c_sclk_capable_pin : natural := 0 -- 0-sclk is generic pin, 1-sclk is clock capable pin 13 | ); 14 | port 15 | ( 16 | clk : in std_logic; 17 | csn, sclk, mosi : in std_logic; 18 | miso : inout std_logic; 19 | rd, wr : out std_logic; 20 | addr : out std_logic_vector(c_addr_bits-1 downto 0); 21 | data_in : in std_logic_vector(7 downto 0); 22 | data_out : out std_logic_vector(7 downto 0) 23 | ); 24 | end; 25 | 26 | architecture syn of spirw_slave is 27 | component spirw_slave_v -- verilog name and its parameters 28 | generic 29 | ( 30 | c_addr_bits : natural; 31 | c_sclk_capable_pin : natural 32 | ); 33 | port 34 | ( 35 | clk : in std_logic; 36 | csn, sclk, mosi : in std_logic; 37 | miso : inout std_logic; 38 | rd, wr : out std_logic; 39 | addr : out std_logic_vector(c_addr_bits-1 downto 0); 40 | data_in : in std_logic_vector(7 downto 0); 41 | data_out : out std_logic_vector(7 downto 0) 42 | ); 43 | end component; 44 | 45 | begin 46 | spirw_slave_v_inst: spirw_slave_v 47 | generic map 48 | ( 49 | c_addr_bits => c_addr_bits, 50 | c_sclk_capable_pin => c_sclk_capable_pin 51 | ) 52 | port map 53 | ( 54 | clk => clk, 55 | csn => csn, sclk => sclk, mosi => mosi, 56 | miso => miso, 57 | rd => rd, wr => wr, 58 | addr => addr, 59 | data_in => data_in, 60 | data_out => data_out 61 | ); 62 | end syn; 63 | -------------------------------------------------------------------------------- /rtl_emard/osd/spirw_slave_v.v: -------------------------------------------------------------------------------- 1 | // SPI RW slave 2 | 3 | // AUTHOR=EMARD 4 | // LICENSE=BSD 5 | 6 | // read with dummy byte which should be discarded 7 | 8 | // write 00 9 | // read 01 10 | 11 | module spirw_slave_v 12 | #( 13 | parameter c_addr_bits = 16, 14 | parameter c_sclk_capable_pin = 0 //, // 0-sclk is generic pin, 1-sclk is clock capable pin 15 | ) 16 | ( 17 | input wire clk, // faster than SPI clock 18 | input wire csn, sclk, mosi, // SPI lines to be sniffed 19 | inout wire miso, // 3-state line, active when csn=0 20 | // BRAM interface 21 | output wire rd, wr, 22 | output wire [c_addr_bits-1:0] addr, 23 | input wire [7:0] data_in, 24 | output wire [7:0] data_out 25 | ); 26 | reg [c_addr_bits:0] R_raddr; 27 | //reg [8-1:0] R_MISO; 28 | //reg [8-1:0] R_MOSI; 29 | reg [8-1:0] R_byte; 30 | reg R_request_read; 31 | reg R_request_write; 32 | localparam c_count_bits=$clog2(c_addr_bits+8)+1; 33 | reg [c_count_bits-1:0] R_bit_count; 34 | generate 35 | if(c_sclk_capable_pin) 36 | begin 37 | // sclk is clock capable pin 38 | always @(posedge sclk or posedge csn) 39 | begin 40 | if(csn) 41 | begin 42 | R_request_read <= 1'b0; 43 | R_request_write <= 1'b0; 44 | R_bit_count <= c_addr_bits+7; // 24 bits = 3 bytes to read cmd/addr, then data 45 | end 46 | else // csn == 0 47 | begin 48 | if(R_request_read) 49 | R_byte <= data_in; 50 | else 51 | R_byte <= { R_byte[8-2:0], mosi }; 52 | if(R_bit_count[c_count_bits-1] == 1'b0) // first 3 bytes 53 | begin 54 | R_raddr <= { R_raddr[c_addr_bits-1:0], mosi }; 55 | R_bit_count <= R_bit_count - 1; 56 | end 57 | else // after first 3 bytes 58 | begin 59 | if(R_bit_count[3:0] == 4'd7) // first bit in new byte, increment address from 5th SPI byte on 60 | R_raddr[c_addr_bits-1:0] <= R_raddr[c_addr_bits-1:0] + 1; 61 | if(R_bit_count[2:0] == 3'd1) 62 | R_request_read <= R_raddr[c_addr_bits]; 63 | else 64 | R_request_read <= 1'b0; 65 | if(R_bit_count[2:0] == 3'd0) // last bit in byte 66 | begin 67 | if(R_raddr[c_addr_bits] == 1'b0) 68 | R_request_write <= 1'b1; // write 69 | R_bit_count[3] <= 1'b0; // allow to inc address from 5th SPI byte on 70 | end 71 | else 72 | R_request_write <= 1'b0; 73 | R_bit_count[2:0] <= R_bit_count[2:0] - 1; 74 | end // after 1st 3 bytes 75 | end // csn = 0, rising sclk edge 76 | end // always 77 | end // generate 78 | else // sclk is not CLK capable pin 79 | begin 80 | // sclk is generic pin 81 | // it needs clock synchronous edge detection 82 | reg R_mosi; 83 | reg [1:0] R_sclk; 84 | always @(posedge clk) 85 | begin 86 | R_sclk <= {R_sclk[0], sclk}; 87 | R_mosi <= mosi; 88 | end 89 | always @(posedge clk) 90 | begin 91 | if(csn) 92 | begin 93 | R_request_read <= 1'b0; 94 | R_request_write <= 1'b0; 95 | R_bit_count <= c_addr_bits+7; // 24 bits = 3 bytes to read cmd/addr, then data 96 | end 97 | else // csn == 0 98 | begin 99 | if(R_sclk == 2'b01) // rising edge 100 | begin 101 | if(R_request_read) 102 | R_byte <= data_in; 103 | else 104 | R_byte <= { R_byte[8-2:0], mosi }; 105 | if(R_bit_count[c_count_bits-1] == 1'b0) // first 3 bytes 106 | begin 107 | R_raddr <= { R_raddr[c_addr_bits-1:0], R_mosi }; 108 | R_bit_count <= R_bit_count - 1; 109 | end 110 | else // after first 3 bytes 111 | begin 112 | if(R_bit_count[3:0] == 4'd7) // first bit in new byte, increment address from 5th SPI byte on 113 | R_raddr[c_addr_bits-1:0] <= R_raddr[c_addr_bits-1:0] + 1; 114 | if(R_bit_count[2:0] == 3'd1) 115 | R_request_read <= R_raddr[c_addr_bits]; 116 | else 117 | R_request_read <= 1'b0; 118 | if(R_bit_count[2:0] == 3'd0) // last bit in byte 119 | begin 120 | if(R_raddr[c_addr_bits] == 1'b0) 121 | R_request_write <= 1'b1; // write 122 | R_bit_count[3] <= 1'b0; // allow to inc address from 5th SPI byte on 123 | end 124 | else 125 | R_request_write <= 1'b0; 126 | R_bit_count[2:0] <= R_bit_count[2:0] - 1; 127 | end // after 1st 3 bytes 128 | end // sclk rising edge 129 | end // csn=0 130 | end // always 131 | end // generate 132 | endgenerate 133 | assign rd = R_request_read; 134 | assign wr = R_request_write; 135 | assign addr = R_raddr[c_addr_bits-1:0]; 136 | assign miso = csn ? 1'bz : R_byte[8-1]; 137 | assign data_out = R_byte; 138 | endmodule 139 | -------------------------------------------------------------------------------- /rtl_emard/spi_display/spi_display_init_pack.vhd: -------------------------------------------------------------------------------- 1 | -- (c) EMARD 2 | -- License=BSD 3 | 4 | library ieee; 5 | use ieee.std_logic_1164.all; 6 | 7 | package spi_display_init_pack is 8 | type T_spi_display_init_seq is array (natural range <>) of std_logic_vector(7 downto 0); 9 | end; 10 | -------------------------------------------------------------------------------- /rtl_emard/spi_display/st7789_init_pack.vhd: -------------------------------------------------------------------------------- 1 | -- (c) EMARD 2 | -- License=BSD 3 | 4 | library ieee; 5 | use ieee.std_logic_1164.all; 6 | 7 | use work.spi_display_init_pack.all; 8 | 9 | -- LCD ST7789 initialization sequence 10 | -- next byte after a NOP command encodes delay in ms 11 | 12 | package st7789_init_pack is 13 | -- all this are commands and should be send with DC line low 14 | constant C_ST7789_SWRESET: std_logic_vector(7 downto 0) := x"01"; 15 | constant C_ST7789_SLPOUT: std_logic_vector(7 downto 0) := x"11"; 16 | constant C_ST7789_COLMOD: std_logic_vector(7 downto 0) := x"3A"; 17 | constant C_ST7789_MADCTL: std_logic_vector(7 downto 0) := x"36"; 18 | constant C_ST7789_CASET_X: std_logic_vector(7 downto 0) := x"2A"; 19 | constant C_ST7789_RASET_Y: std_logic_vector(7 downto 0) := x"2B"; 20 | constant C_ST7789_INVON: std_logic_vector(7 downto 0) := x"13"; 21 | constant C_ST7789_DISPON: std_logic_vector(7 downto 0) := x"29"; 22 | constant C_ST7789_RAMWR: std_logic_vector(7 downto 0) := x"2C"; 23 | 24 | constant C_st7789_init_seq: T_spi_display_init_seq := 25 | ( 26 | -- after reset, delay 2^13 us = 8ms before sending commands 27 | x"80", 28 | x"0D", 29 | -- SWRESET, delay 2^17 us = 131us 30 | x"01", 31 | x"80", 32 | x"11", 33 | -- SLPOUT, delay 2^14 us = 16ms 34 | x"11", 35 | x"80", 36 | x"0E", 37 | -- COLMOD, 16-bit color, delay 2^14 us = 16ms 38 | x"3A", 39 | x"81", 40 | x"55", 41 | x"0E", 42 | -- MADCTL 43 | x"36", 44 | x"01", 45 | x"C0", 46 | -- CASET X 47 | x"2A", 48 | x"04", 49 | -- X start MSB,LSB 50 | x"00", 51 | x"00", 52 | -- X end MSB,LSB 53 | x"00", 54 | x"EF", 55 | -- RASET Y 56 | x"2B", 57 | x"04", 58 | -- Y start MSB,LSB 59 | x"00", 60 | x"50", 61 | -- Y end MSB,LSB 62 | x"01", 63 | x"3F", 64 | -- INVON, delay 2^14 us = 16ms 65 | x"21", 66 | x"80", 67 | x"0E", 68 | -- NORON, delay 2^14 us = 16ms 69 | x"13", 70 | x"80", 71 | x"0E", 72 | -- DISPON, delay 2^14 us = 16ms 73 | x"29", 74 | x"80", 75 | x"0E", 76 | -- RAMWR 2C 00 77 | x"2C", 78 | x"00" 79 | ); 80 | end; 81 | -------------------------------------------------------------------------------- /rtl_emard/usb/usb11_phy_vhdl/usb_phy_transciver.vhd: -------------------------------------------------------------------------------- 1 | -- (c)EMARD 2 | -- License=GPL 3 | 4 | -- glued FPGA transiever and USB11 soft core PHY 5 | 6 | library ieee; 7 | use ieee.std_logic_1164.all; 8 | use ieee.std_logic_unsigned.all; 9 | use ieee.numeric_std.ALL; 10 | 11 | library work; 12 | 13 | entity usb11_phy_transciever is 14 | generic 15 | ( 16 | C_usb_speed: std_logic := '0' -- '0':6 MHz low speed '1':48 MHz full speed 17 | ); 18 | port 19 | ( 20 | clk : in std_logic; -- main clock input 21 | resetn : in std_logic; 22 | -- USB hardware D+/D- direct to FPGA 23 | usb_dif : in std_logic; -- differential or single-ended input 24 | usb_dp, usb_dn : inout std_logic; -- single ended bidirectional 25 | -- UTMI interface 26 | utmi_txready_o : out std_logic; 27 | utmi_data_o : out std_logic_vector(7 downto 0); 28 | utmi_rxvalid_o : out std_logic; 29 | utmi_rxactive_o : out std_logic; 30 | utmi_rxerror_o : out std_logic; 31 | utmi_linestate_o : out std_logic_vector(1 downto 0); 32 | utmi_linectrl_i : in std_logic; 33 | utmi_data_i : in std_logic_vector(7 downto 0); 34 | utmi_txvalid_i : in std_logic; 35 | -- UTMI debug 36 | txoe_o, ce_o: out std_logic; 37 | utmi_sync_err_o, utmi_bit_stuff_err_o, utmi_byte_err_o: out std_logic 38 | ); 39 | end; 40 | 41 | architecture Behavioral of usb11_phy_transciever is 42 | signal S_rxd: std_logic; 43 | signal S_rxdp, S_rxdn: std_logic; 44 | signal S_txdp, S_txdn, S_txoe: std_logic; 45 | begin 46 | G_usb_full_speed: if C_usb_speed = '1' generate 47 | -- transciever soft-core 48 | --usb_fpga_pu_dp <= '0'; -- D+ pulldown for USB host mode 49 | --usb_fpga_pu_dn <= '0'; -- D- pulldown for USB host mode 50 | S_rxd <= usb_dif; -- differential input reads D+ 51 | --S_rxd <= usb_dp; -- single-ended input reads D+ may work as well 52 | S_rxdp <= usb_dp; -- single-ended input reads D+ 53 | S_rxdn <= usb_dn; -- single-ended input reads D- 54 | usb_dp <= S_txdp when S_txoe = '0' else 'Z'; 55 | usb_dn <= S_txdn when S_txoe = '0' else 'Z'; 56 | end generate; 57 | 58 | G_usb_low_speed: if C_usb_speed = '0' generate 59 | -- transciever soft-core 60 | -- for low speed USB, here are swaped D+ and D- 61 | --usb_fpga_pu_dp <= '0'; -- D+ pulldown for USB host mode 62 | --usb_fpga_pu_dn <= '0'; -- D- pulldown for USB host mode 63 | S_rxd <= not usb_dif; -- differential input reads inverted D+ for low speed 64 | --S_rxd <= not usb_dp; -- single-ended input reads D+ may work as well 65 | S_rxdp <= usb_dn; -- single-ended input reads D- for low speed 66 | S_rxdn <= usb_dp; -- single-ended input reads D+ for low speed 67 | usb_dp <= S_txdn when S_txoe = '0' else 'Z'; 68 | usb_dn <= S_txdp when S_txoe = '0' else 'Z'; 69 | end generate; 70 | 71 | -- USB1.1 PHY soft-core 72 | usb11_phy: entity work.usb_phy 73 | generic map 74 | ( 75 | usb_rst_det => false 76 | ) 77 | port map 78 | ( 79 | clk => clk, -- full speed: 48 MHz or 60 MHz, low speed: 6 MHz or 7.5 MHz 80 | rst => resetn, -- 1-don't reset, 0-hold reset 81 | phy_tx_mode => '1', -- 1-differential, 0-single-ended 82 | usb_rst => open, -- USB host requests reset, sending signal to usb-serial core 83 | -- clock recovery debug 84 | ce_o => ce_o, 85 | -- UTMI interface to usb-serial core 86 | LineCtrl_i => utmi_linectrl_i, 87 | TxValid_i => utmi_txvalid_i, 88 | DataOut_i => utmi_data_i, -- 8-bit TX 89 | TxReady_o => utmi_txready_o, 90 | RxValid_o => utmi_rxvalid_o, 91 | DataIn_o => utmi_data_o, -- 8-bit RX 92 | RxActive_o => utmi_rxactive_o, 93 | RxError_o => utmi_rxerror_o, 94 | LineState_o => utmi_linestate_o, -- 2-bit 95 | -- debug interface 96 | sync_err_o => utmi_sync_err_o, 97 | bit_stuff_err_o => utmi_bit_stuff_err_o, 98 | byte_err_o => utmi_byte_err_o, 99 | -- transciever interface to hardware 100 | rxd => S_rxd, -- differential input from D+ 101 | rxdp => S_rxdp, -- single-ended input from D+ 102 | rxdn => S_rxdn, -- single-ended input from D- 103 | txdp => S_txdp, -- single-ended output to D+ 104 | txdn => S_txdn, -- single-ended output to D- 105 | txoe => S_txoe -- 3-state control: 0-output, 1-input 106 | ); 107 | txoe_o <= S_txoe; 108 | end; 109 | -------------------------------------------------------------------------------- /rtl_emard/usb/usbhid/report_decoded_pack_generic.vhd: -------------------------------------------------------------------------------- 1 | -- (c) EMARD 2 | -- License=BSD 3 | 4 | library ieee; 5 | use ieee.std_logic_1164.all; 6 | 7 | package report_decoded_pack is 8 | type T_report_decoded is 9 | record 10 | lstick_x, lstick_y, rstick_x, rstick_y: std_logic_vector(7 downto 0); -- up/left=0 idle=128 down/right=255 11 | lmouseq_x, lmouseq_y, rmouseq_x, rmouseq_y: std_logic_vector(1 downto 0); -- stick to quadrature encoder output 12 | analog_ltrigger, analog_rtrigger: std_logic_vector(7 downto 0); 13 | hat_up, hat_down, hat_left, hat_right: std_logic; 14 | lstick_up, lstick_down, lstick_left, lstick_right: std_logic; 15 | rstick_up, rstick_down, rstick_left, rstick_right: std_logic; 16 | btn_a, btn_b, btn_x, btn_y: std_logic; 17 | btn_lbumper, btn_rbumper: std_logic; 18 | btn_ltrigger, btn_rtrigger: std_logic; 19 | btn_back, btn_start: std_logic; 20 | btn_lstick, btn_rstick: std_logic; 21 | btn_fps, btn_fps_toggle: std_logic; 22 | btn_lmouse_left, btn_lmouse_right: std_logic; 23 | btn_rmouse_left, btn_rmouse_right: std_logic; 24 | end record; 25 | end; 26 | -------------------------------------------------------------------------------- /rtl_emard/usb/usbhid/usbhid_report_decoder_nes_joystick.vhd: -------------------------------------------------------------------------------- 1 | library IEEE; 2 | use IEEE.std_logic_1164.ALL; 3 | use IEEE.std_logic_arith.ALL; 4 | use IEEE.std_logic_unsigned.ALL; 5 | 6 | use work.report_decoded_pack.all; 7 | 8 | entity usbhid_report_decoder is 9 | generic 10 | ( 11 | C_reg_input: boolean := true; -- take input in register (release timing) 12 | -- mouse speed also depends on clk 13 | C_lmouse: boolean := false; 14 | C_lmousex_scaler: integer := 23; -- less -> faster mouse 15 | C_lmousey_scaler: integer := 23; -- less -> faster mouse 16 | C_rmouse: boolean := false; 17 | C_rmousex_scaler: integer := 23; -- less -> faster mouse 18 | C_rmousey_scaler: integer := 23 -- less -> faster mouse 19 | ); 20 | port 21 | ( 22 | clk: in std_logic; -- USB host core clock domain 23 | hid_report: in std_logic_vector; 24 | hid_valid: in std_logic; 25 | decoded: out T_report_decoded 26 | ); 27 | end; 28 | 29 | architecture rtl of usbhid_report_decoder is 30 | signal R_hid_report: std_logic_vector(hid_report'range); 31 | signal R_hid_valid: std_logic; 32 | alias S_lstick_x: std_logic_vector(7 downto 0) is R_hid_report(31 downto 24); 33 | alias S_lstick_y: std_logic_vector(7 downto 0) is R_hid_report(39 downto 32); 34 | alias S_btn_a: std_logic is R_hid_report(45); 35 | alias S_btn_b: std_logic is R_hid_report(44); 36 | alias S_btn_select: std_logic is R_hid_report(54); 37 | alias S_btn_start: std_logic is R_hid_report(53); 38 | begin 39 | 40 | yes_reg_input: if C_reg_input generate 41 | process(clk) is 42 | begin 43 | if rising_edge(clk) then 44 | if hid_valid = '1' then 45 | R_hid_report <= hid_report; -- register to release timing closure 46 | end if; 47 | R_hid_valid <= hid_valid; 48 | end if; 49 | end process; 50 | end generate; 51 | 52 | no_reg_input: if not C_reg_input generate 53 | R_hid_report <= hid_report; -- directly take input 54 | R_hid_valid <= hid_valid; 55 | end generate; 56 | 57 | -- hat 58 | decoded.lstick_x <= S_lstick_x; 59 | decoded.lstick_y <= S_lstick_y; 60 | -- simple buttons 61 | decoded.btn_a <= S_btn_a; 62 | decoded.btn_b <= S_btn_b; 63 | decoded.btn_back <= S_btn_select; 64 | decoded.btn_start <= S_btn_start; 65 | 66 | end rtl; 67 | -------------------------------------------------------------------------------- /rtl_emard/usb/usbhid/usbhid_report_decoder_xbox360_joystick.vhd: -------------------------------------------------------------------------------- 1 | library IEEE; 2 | use IEEE.std_logic_1164.ALL; 3 | use IEEE.std_logic_arith.ALL; 4 | use IEEE.std_logic_unsigned.ALL; 5 | 6 | use work.report_decoded_pack.all; 7 | 8 | entity usbhid_report_decoder is 9 | generic 10 | ( 11 | C_reg_input: boolean := true; -- take input in register (release timing) 12 | -- mouse speed also depends on clk 13 | C_lmouse: boolean := false; 14 | C_lmousex_scaler: integer := 23; -- less -> faster mouse 15 | C_lmousey_scaler: integer := 23; -- less -> faster mouse 16 | C_rmouse: boolean := false; 17 | C_rmousex_scaler: integer := 23; -- less -> faster mouse 18 | C_rmousey_scaler: integer := 23 -- less -> faster mouse 19 | ); 20 | port 21 | ( 22 | clk: in std_logic; -- USB host core clock domain 23 | hid_report: in std_logic_vector; 24 | hid_valid: in std_logic; 25 | decoded: out T_report_decoded 26 | ); 27 | end; 28 | 29 | architecture rtl of usbhid_report_decoder is 30 | signal R_hid_report: std_logic_vector(hid_report'range); 31 | signal R_hid_valid: std_logic; 32 | alias S_lstick_x: std_logic_vector(7 downto 0) is R_hid_report(63 downto 56); 33 | alias S_lstick_y: std_logic_vector(7 downto 0) is R_hid_report(79 downto 72); 34 | -- constant S_lstick_x: std_logic_vector(7 downto 0) := x"00"; 35 | -- constant S_lstick_y: std_logic_vector(7 downto 0) := x"00"; 36 | alias S_rstick_x: std_logic_vector(7 downto 0) is R_hid_report(95 downto 88); 37 | alias S_rstick_y: std_logic_vector(7 downto 0) is R_hid_report(111 downto 104); 38 | alias S_analog_ltrigger: std_logic_vector(7 downto 0) is R_hid_report(39 downto 32); 39 | alias S_analog_rtrigger: std_logic_vector(7 downto 0) is R_hid_report(47 downto 40); 40 | alias S_btn_x: std_logic is R_hid_report(30); 41 | alias S_btn_a: std_logic is R_hid_report(28); 42 | alias S_btn_b: std_logic is R_hid_report(29); 43 | alias S_btn_y: std_logic is R_hid_report(31); 44 | alias S_btn_lbumper: std_logic is R_hid_report(24); 45 | alias S_btn_rbumper: std_logic is R_hid_report(25); 46 | alias S_btn_ltrigger: std_logic is R_hid_report(39); 47 | alias S_btn_rtrigger: std_logic is R_hid_report(47); 48 | alias S_btn_back: std_logic is R_hid_report(21); 49 | alias S_btn_start: std_logic is R_hid_report(20); 50 | alias S_btn_lstick: std_logic is R_hid_report(22); 51 | alias S_btn_rstick: std_logic is R_hid_report(23); 52 | alias S_btn_fps: std_logic is R_hid_report(26); 53 | alias S_hat_up: std_logic is R_hid_report(16); 54 | alias S_hat_down: std_logic is R_hid_report(17); 55 | alias S_hat_left: std_logic is R_hid_report(18); 56 | alias S_hat_right: std_logic is R_hid_report(19); 57 | signal S_hat_udlr: std_logic_vector(3 downto 0); -- decoded 58 | -- decoded stick to digital 59 | signal S_lstick_up, S_lstick_down, S_lstick_left, S_lstick_right: std_logic; 60 | signal S_rstick_up, S_rstick_down, S_rstick_left, S_rstick_right: std_logic; 61 | signal R_lmousecx: std_logic_vector(C_lmousex_scaler-1 downto 0); 62 | signal R_lmousecy: std_logic_vector(C_lmousey_scaler-1 downto 0); 63 | signal R_rmousecx: std_logic_vector(C_rmousex_scaler-1 downto 0); 64 | signal R_rmousecy: std_logic_vector(C_rmousey_scaler-1 downto 0); 65 | begin 66 | 67 | yes_reg_input: if C_reg_input generate 68 | process(clk) is 69 | begin 70 | if rising_edge(clk) then 71 | -- if hid_valid = '1' then 72 | R_hid_report <= hid_report; -- register to release timing closure 73 | -- end if; 74 | R_hid_valid <= hid_valid; 75 | end if; 76 | end process; 77 | end generate; 78 | 79 | no_reg_input: if not C_reg_input generate 80 | R_hid_report <= hid_report; -- directly take input 81 | R_hid_valid <= hid_valid; 82 | end generate; 83 | 84 | -- simple buttons 85 | decoded.btn_x <= S_btn_x; 86 | decoded.btn_a <= S_btn_a; 87 | decoded.btn_b <= S_btn_b; 88 | decoded.btn_y <= S_btn_y; 89 | decoded.btn_lbumper <= S_btn_lbumper; 90 | decoded.btn_rbumper <= S_btn_rbumper; 91 | decoded.btn_ltrigger <= S_btn_ltrigger; 92 | decoded.btn_rtrigger <= S_btn_rtrigger; 93 | decoded.btn_back <= S_btn_back; 94 | decoded.btn_start <= S_btn_start; 95 | decoded.btn_lstick <= S_btn_lstick; 96 | decoded.btn_rstick <= S_btn_rstick; 97 | decoded.btn_fps <= S_btn_fps; 98 | decoded.btn_fps_toggle <= '0'; 99 | 100 | -- hat decoder 101 | S_hat_udlr(3) <= S_hat_up; 102 | S_hat_udlr(2) <= S_hat_down; 103 | S_hat_udlr(1) <= S_hat_left; 104 | S_hat_udlr(0) <= S_hat_right; 105 | 106 | -- hat as buttons 107 | decoded.hat_up <= S_hat_up; 108 | decoded.hat_down <= S_hat_down; 109 | decoded.hat_left <= S_hat_left; 110 | decoded.hat_right <= S_hat_right; 111 | 112 | -- analog stick to digital decoders 113 | -- down left negative, up right positive 114 | decoded.lstick_left <= '1' when S_lstick_x(7 downto 5) = "100" else '0'; 115 | decoded.lstick_right <= '1' when S_lstick_x(7 downto 5) = "011" else '0'; 116 | decoded.lstick_up <= '1' when S_lstick_y(7 downto 5) = "011" else '0'; 117 | decoded.lstick_down <= '1' when S_lstick_y(7 downto 5) = "100" else '0'; 118 | decoded.rstick_left <= '1' when S_rstick_x(7 downto 5) = "100" else '0'; 119 | decoded.rstick_right <= '1' when S_rstick_x(7 downto 5) = "011" else '0'; 120 | decoded.rstick_up <= '1' when S_rstick_y(7 downto 5) = "011" else '0'; 121 | decoded.rstick_down <= '1' when S_rstick_y(7 downto 5) = "100" else '0'; 122 | 123 | decoded.analog_ltrigger <= S_analog_ltrigger; 124 | decoded.analog_rtrigger <= S_analog_rtrigger; 125 | 126 | yes_lmouse: if C_lmouse generate 127 | -- mouse counters 128 | process(clk) 129 | begin 130 | if rising_edge(clk) then 131 | R_lmousecx <= R_lmousecx+S_lstick_x-128; 132 | R_lmousecy <= R_lmousecy+S_lstick_y-128; 133 | end if; 134 | end process; 135 | 136 | decoded.btn_lmouse_left <= S_btn_lstick; 137 | decoded.btn_lmouse_right <= S_btn_lbumper; 138 | -- mouse quadrature encoders 139 | decoded.lmouseq_x <= "01" when R_lmousecx(R_lmousecx'high downto R_lmousecx'high-1) = "00" else 140 | "11" when R_lmousecx(R_lmousecx'high downto R_lmousecx'high-1) = "01" else 141 | "10" when R_lmousecx(R_lmousecx'high downto R_lmousecx'high-1) = "10" else 142 | "00"; -- when "11" 143 | decoded.lmouseq_y <= "01" when R_lmousecy(R_lmousecy'high downto R_lmousecy'high-1) = "00" else 144 | "11" when R_lmousecy(R_lmousecy'high downto R_lmousecy'high-1) = "01" else 145 | "10" when R_lmousecy(R_lmousecy'high downto R_lmousecy'high-1) = "10" else 146 | "00"; -- when "11" 147 | end generate; 148 | 149 | yes_rmouse: if C_rmouse generate 150 | -- mouse counters 151 | process(clk) 152 | begin 153 | if rising_edge(clk) then 154 | R_rmousecx <= R_rmousecx+S_rstick_x-128; 155 | R_rmousecy <= R_rmousecy+S_rstick_y-128; 156 | end if; 157 | end process; 158 | 159 | decoded.btn_rmouse_left <= S_btn_rstick; 160 | decoded.btn_rmouse_right <= S_btn_rbumper; 161 | -- mouse quadrature encoders 162 | decoded.rmouseq_x <= "01" when R_rmousecx(R_rmousecx'high downto R_rmousecx'high-1) = "00" else 163 | "11" when R_rmousecx(R_rmousecx'high downto R_rmousecx'high-1) = "01" else 164 | "10" when R_rmousecx(R_rmousecx'high downto R_rmousecx'high-1) = "10" else 165 | "00"; -- when "11" 166 | decoded.rmouseq_y <= "01" when R_rmousecy(R_rmousecy'high downto R_rmousecy'high-1) = "00" else 167 | "11" when R_rmousecy(R_rmousecy'high downto R_rmousecy'high-1) = "01" else 168 | "10" when R_rmousecy(R_rmousecy'high downto R_rmousecy'high-1) = "10" else 169 | "00"; -- when "11" 170 | end generate; 171 | 172 | end rtl; 173 | -------------------------------------------------------------------------------- /rtl_emard/usb/usbhost/usbh_crc16.v: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // USB Full Speed Host 3 | // V0.5 4 | // Ultra-Embedded.com 5 | // Copyright 2015-2019 6 | // 7 | // Email: admin@ultra-embedded.com 8 | // 9 | // License: GPL 10 | // If you would like a version with a more permissive license for 11 | // use in closed source commercial applications please contact me 12 | // for details. 13 | //----------------------------------------------------------------- 14 | // 15 | // This file is open source HDL; you can redistribute it and/or 16 | // modify it under the terms of the GNU General Public License as 17 | // published by the Free Software Foundation; either version 2 of 18 | // the License, or (at your option) any later version. 19 | // 20 | // This file is distributed in the hope that it will be useful, 21 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | // GNU General Public License for more details. 24 | // 25 | // You should have received a copy of the GNU General Public 26 | // License along with this file; if not, write to the Free Software 27 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 28 | // USA 29 | //----------------------------------------------------------------- 30 | 31 | //----------------------------------------------------------------- 32 | // Generated File 33 | //----------------------------------------------------------------- 34 | //----------------------------------------------------------------- 35 | // Module: 16-bit CRC used by USB data packets 36 | //----------------------------------------------------------------- 37 | module usbh_crc16 38 | ( 39 | input [15:0] crc_i, 40 | input [7:0] data_i, 41 | output [15:0] crc_o 42 | ); 43 | 44 | //----------------------------------------------------------------- 45 | // Implementation 46 | //----------------------------------------------------------------- 47 | assign crc_o[15] = data_i[0] ^ data_i[1] ^ data_i[2] ^ data_i[3] ^ data_i[4] ^ 48 | data_i[5] ^ data_i[6] ^ data_i[7] ^ crc_i[7] ^ crc_i[6] ^ 49 | crc_i[5] ^ crc_i[4] ^ crc_i[3] ^ crc_i[2] ^ 50 | crc_i[1] ^ crc_i[0]; 51 | assign crc_o[14] = data_i[0] ^ data_i[1] ^ data_i[2] ^ data_i[3] ^ data_i[4] ^ data_i[5] ^ 52 | data_i[6] ^ crc_i[6] ^ crc_i[5] ^ crc_i[4] ^ 53 | crc_i[3] ^ crc_i[2] ^ crc_i[1] ^ crc_i[0]; 54 | assign crc_o[13] = data_i[6] ^ data_i[7] ^ crc_i[7] ^ crc_i[6]; 55 | assign crc_o[12] = data_i[5] ^ data_i[6] ^ crc_i[6] ^ crc_i[5]; 56 | assign crc_o[11] = data_i[4] ^ data_i[5] ^ crc_i[5] ^ crc_i[4]; 57 | assign crc_o[10] = data_i[3] ^ data_i[4] ^ crc_i[4] ^ crc_i[3]; 58 | assign crc_o[9] = data_i[2] ^ data_i[3] ^ crc_i[3] ^ crc_i[2]; 59 | assign crc_o[8] = data_i[1] ^ data_i[2] ^ crc_i[2] ^ crc_i[1]; 60 | assign crc_o[7] = data_i[0] ^ data_i[1] ^ crc_i[15] ^ crc_i[1] ^ crc_i[0]; 61 | assign crc_o[6] = data_i[0] ^ crc_i[14] ^ crc_i[0]; 62 | assign crc_o[5] = crc_i[13]; 63 | assign crc_o[4] = crc_i[12]; 64 | assign crc_o[3] = crc_i[11]; 65 | assign crc_o[2] = crc_i[10]; 66 | assign crc_o[1] = crc_i[9]; 67 | assign crc_o[0] = data_i[0] ^ data_i[1] ^ data_i[2] ^ data_i[3] ^ data_i[4] ^ data_i[5] ^ 68 | data_i[6] ^ data_i[7] ^ crc_i[8] ^ crc_i[7] ^ crc_i[6] ^ 69 | crc_i[5] ^ crc_i[4] ^ crc_i[3] ^ crc_i[2] ^ 70 | crc_i[1] ^ crc_i[0]; 71 | 72 | endmodule 73 | 74 | -------------------------------------------------------------------------------- /rtl_emard/usb/usbhost/usbh_crc5.v: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // USB Full Speed Host 3 | // V0.5 4 | // Ultra-Embedded.com 5 | // Copyright 2015-2019 6 | // 7 | // Email: admin@ultra-embedded.com 8 | // 9 | // License: GPL 10 | // If you would like a version with a more permissive license for 11 | // use in closed source commercial applications please contact me 12 | // for details. 13 | //----------------------------------------------------------------- 14 | // 15 | // This file is open source HDL; you can redistribute it and/or 16 | // modify it under the terms of the GNU General Public License as 17 | // published by the Free Software Foundation; either version 2 of 18 | // the License, or (at your option) any later version. 19 | // 20 | // This file is distributed in the hope that it will be useful, 21 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | // GNU General Public License for more details. 24 | // 25 | // You should have received a copy of the GNU General Public 26 | // License along with this file; if not, write to the Free Software 27 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 28 | // USA 29 | //----------------------------------------------------------------- 30 | 31 | //----------------------------------------------------------------- 32 | // Generated File 33 | //----------------------------------------------------------------- 34 | //----------------------------------------------------------------- 35 | // Module: 5-bit CRC used by USB tokens 36 | //----------------------------------------------------------------- 37 | module usbh_crc5 38 | ( 39 | input [4:0] crc_i, 40 | input [10:0] data_i, 41 | output [4:0] crc_o 42 | ); 43 | 44 | //----------------------------------------------------------------- 45 | // Implementation 46 | //----------------------------------------------------------------- 47 | assign crc_o[0] = data_i[10] ^ data_i[9] ^ data_i[6] ^ data_i[5] ^ data_i[3] ^ data_i[0] ^ 48 | crc_i[0] ^ crc_i[3] ^ crc_i[4]; 49 | 50 | assign crc_o[1] = data_i[10] ^ data_i[7] ^ data_i[6] ^ data_i[4] ^ data_i[1] ^ 51 | crc_i[0] ^ crc_i[1] ^ crc_i[4]; 52 | 53 | assign crc_o[2] = data_i[10] ^ data_i[9] ^ data_i[8] ^ data_i[7] ^ data_i[6] ^ data_i[3] ^ data_i[2] ^ data_i[0] ^ 54 | crc_i[0] ^ crc_i[1] ^ crc_i[2] ^ crc_i[3] ^ crc_i[4]; 55 | 56 | assign crc_o[3] = data_i[10] ^ data_i[9] ^ data_i[8] ^ data_i[7] ^ data_i[4] ^ data_i[3] ^ data_i[1] ^ 57 | crc_i[1] ^ crc_i[2] ^ crc_i[3] ^ crc_i[4]; 58 | 59 | assign crc_o[4] = data_i[10] ^ data_i[9] ^ data_i[8] ^ data_i[5] ^ data_i[4] ^ data_i[2] ^ 60 | crc_i[2] ^ crc_i[3] ^ crc_i[4]; 61 | 62 | endmodule 63 | 64 | -------------------------------------------------------------------------------- /rtl_emard/usb/usbhost/usbh_setup_pack.vhd: -------------------------------------------------------------------------------- 1 | -- (c) EMARD 2 | -- License=GPL 3 | 4 | -- USB setup (enumeration) sequence 5 | -- for common USB low-speed HID devices. 6 | 7 | -- Works for: 8 | -- Mouse: Logitech: M-BT58, LX3, RX250, Microsoft: IntelliMouse 9 | -- Keyboard: Logitech: NetPlay Y-UC29, Openhardware: V-USB 10 | -- Gamepad: Saitek: P3600, Darfon 11 | 12 | -- Doesn't work for: 13 | -- Keyboard: Logitech: UltraX Y-BL49A 14 | 15 | -- After this minimal setup sequence is replayed to mouse, 16 | -- mouse will answer each IN transfer with HID report 17 | -- or with NAK if report data is not available. 18 | 19 | library ieee; 20 | use ieee.std_logic_1164.all; 21 | use IEEE.NUMERIC_STD.ALL; 22 | 23 | package usbh_setup_pack is 24 | 25 | constant C_setup_retry : integer := 4; -- 2**n retries 3:8 setup retries and then detach 26 | constant C_setup_interval : integer := 17; -- 2**n clocks 15:5.46 ms wait before sending next setup request 27 | constant C_report_interval : integer := 16; -- 2**n clocks 15:5.46 ms wait before sending next request for report 28 | constant C_report_endpoint : integer := 1; -- default=1 endpoint which answers IN transfer with HID report 29 | constant C_report_length : integer := 8; -- report buffer length: 4:M-BT58, 5:LX3, 8:keyboard, 20:XBOX360 30 | 31 | constant C_keepalive_setup : std_logic := '1'; -- enable keepalive during setup 32 | constant C_keepalive_status : std_logic := '1'; -- enable keepalive during setup status OUT 0-length 33 | constant C_keepalive_report : std_logic := '1'; -- enable keepalive during report IN 34 | constant C_keepalive_type : std_logic := '1'; -- '0':SOF-packet (full speed) '1':SE0-pulse (low speed) 35 | constant C_keepalive_phase : std_logic_vector(11 downto 0) := x"7C0"; -- near half 0.68 ms interval (at low speed). max x"FCC" 36 | --constant C_keepalive_phase : std_logic_vector(14 downto 0) := "111" & x"7C0"; -- near half 0.68 ms interval (at full speed) 37 | 38 | type T_setup_rom is array(natural range <>) of std_logic_vector(7 downto 0); 39 | constant C_setup_rom: T_setup_rom := 40 | ( 41 | -- set configuration 1 -- 42 | -- x"00", x"09", x"01", x"00", x"00", x"00", x"00", x"00", 43 | -- HOST: < SYNC >EP0 CRC5 44 | -- D+ ___-_-_-_---___--_-_-_-_-_-_-_--_-_____ 45 | -- D- ---_-_-_-___---__-_-_-_-_-_-_-__-_-__-- 46 | -- HOST: < SYNC >< 00 >< 09 >< 01 >< 00 >< 00 >< 00 >< 00 >< 00 >< CRC16 > 47 | -- D+ ___-_-_-_----_-_---_-_-_-_--_--_-_--_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-____-__-__--_--_-____ 48 | -- D- ---_-_-_-____-_-___-_-_-_-__-__-_-__-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_----_--_--__-__-___-- 49 | -- MOUSE: < SYNC >< ACK > 50 | -- D+ ___-_-_-_--__-__---_____ 51 | -- D- ---_-_-_-__--_--_____--- 52 | -- set idle 0 -- 53 | -- x"21", x"0A", x"00", x"00", x"00", x"00", x"00", x"00" 54 | -- HOST: < SYNC >EP0 CRC5 55 | -- D+ ___-_-_-_---___--_-_-_-_-_-_-_--_-_____ 56 | -- D- ---_-_-_-___---__-_-_-_-_-_-_-__-_-__-- 57 | -- HOST: < SYNC >< 21 >< 0A >< 00 >< 00 >< 00 >< 00 >< 00 >< 00 >< CRC16 > 58 | -- D+ ___-_-_-_----_-_----_-_--_-__--_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-___--___-_-_--_-____ 59 | -- D- ---_-_-_-____-_-____-_-__-_--__-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_---__---_-_-__-___-- 60 | -- MOUSE: < SYNC >< ACK > 61 | -- D+ ___-_-_-_--__-__---_____ 62 | -- D- ---_-_-_-__--_--_____--- 63 | -- set report request 0x200 -- 64 | -- x"21", x"09", x"00", x"02", x"00", x"00", x"01", x"00" 65 | -- HOST: < SYNC >EP0 CRC5 66 | -- D+ ___-_-_-_---___--_-_-_-_-_-_-_--_-_____ 67 | -- D- ---_-_-_-___---__-_-_-_-_-_-_-__-_-__-- 68 | -- HOST: < SYNC >< 21 >< 09 >< 00 >< 02 >< 00 >< 00 >< 01 >< 00 >< CRC16 > 69 | -- D+ ___-_-_-_----_-_----_-_--_--_--_-_-_-_-_-_-__-_-_-_-_-_-_-_-_-_-_-__-_-_-_-_-_-_-_--____-__-_-____-___ 70 | -- D- ---_-_-_-____-_-____-_-__-__-__-_-_-_-_-_-_--_-_-_-_-_-_-_-_-_-_-_--_-_-_-_-_-_-_-__----_--_-_----__-- 71 | -- MOUSE: < SYNC >< ACK > 72 | -- D+ ___-_-_-_--__-__---_____ 73 | -- D- ---_-_-_-__--_--_____--- 74 | -- set_address 1, Microsoft IntelliMouse needs address > 0 to activate reports -- 75 | x"00", x"05", x"01", x"00", x"00", x"00", x"00", x"00", 76 | -- get_device_descriptor, requested length 0x12 = 18 bytes, no known device needs this -- 77 | -- x"80", x"06", x"00", x"01", x"00", x"00", x"12", x"00", 78 | -- set report request 0x200 with 1 byte data 0x00, no known device needs this -- 79 | -- x"21", x"09", x"00", x"02", x"00", x"00", x"01", x"00", x"00", 80 | -- set_configuration 1, most devices need configuration = 1 to activate reports -- 81 | x"00", x"09", x"01", x"00", x"00", x"00", x"00", x"00" 82 | -- NOTE: last setup packet currently must be a non-data phase packet 83 | -- like set configuration, there's a bug that skips data phase at last packet 84 | ); 85 | 86 | -- to generate this package: 87 | -- modprobe usbmon 88 | -- chown user:user /dev/usbmon* 89 | -- wireshark 90 | -- plug USB device and press buttons, move it or replug few times 91 | -- to find out which usbmon device receives its traffic, 92 | -- then select it to capture 93 | -- plug USB device 94 | -- find 8-byte data from sniffed "URB setup" source host 95 | -- e.g. 80 06 00 01 00 00 12 00 and copy it here as x"80", x"06", ... 96 | 97 | -- USB hid descriptor describes data format of the report: 98 | -- apt-get install usbutils 99 | -- usbhid-dump 100 | -- copy hex output to online USB descriptor parser 101 | -- https://eleccelerator.com/usbdescreqparser/ 102 | 103 | end; 104 | -------------------------------------------------------------------------------- /rtl_emard/usb/usbhost/usbh_sie_vhdl.vhd: -------------------------------------------------------------------------------- 1 | -- 2 | -- AUTHOR=EMARD 3 | -- LICENSE=BSD 4 | -- 5 | 6 | -- VHDL Wrapper for usb_sie 7 | -- reverses bits order of dev addr and ep 8 | 9 | LIBRARY ieee; 10 | USE ieee.std_logic_1164.all; 11 | use ieee.std_logic_unsigned.all; 12 | use ieee.std_logic_arith.all; 13 | 14 | entity usbh_sie_vhdl is 15 | port 16 | ( 17 | clk_i : in std_logic; 18 | rst_i : in std_logic; 19 | start_i : in std_logic; 20 | in_transfer_i : in std_logic; 21 | sof_transfer_i : in std_logic; 22 | resp_expected_i : in std_logic; 23 | token_pid_i : in std_logic_vector(7 downto 0); 24 | token_dev_i : in std_logic_vector(6 downto 0); 25 | token_ep_i : in std_logic_vector(3 downto 0); 26 | data_len_i : in std_logic_vector(15 downto 0); 27 | data_idx_i : in std_logic; 28 | tx_data_i : in std_logic_vector(7 downto 0); 29 | utmi_txready_i : in std_logic; 30 | utmi_data_i : in std_logic_vector(7 downto 0); 31 | utmi_rxvalid_i : in std_logic; 32 | utmi_rxactive_i : in std_logic; 33 | 34 | ack_o : out std_logic; 35 | tx_pop_o : out std_logic; 36 | rx_data_o : out std_logic_vector(7 downto 0); 37 | rx_push_o : out std_logic; 38 | tx_done_o : out std_logic; 39 | rx_done_o : out std_logic; 40 | crc_err_o : out std_logic; 41 | timeout_o : out std_logic; 42 | response_o : out std_logic_vector(7 downto 0); 43 | rx_count_o : out std_logic_vector(15 downto 0); 44 | idle_o : out std_logic; 45 | utmi_linectrl_o : out std_logic; 46 | utmi_data_o : out std_logic_vector(7 downto 0); 47 | utmi_txvalid_o : out std_logic 48 | ); 49 | end; 50 | 51 | architecture syn of usbh_sie_vhdl is 52 | component usbh_sie -- verilog name and its parameters 53 | port ( 54 | clk_i : in std_logic; 55 | rst_i : in std_logic; 56 | start_i : in std_logic; 57 | in_transfer_i : in std_logic; 58 | sof_transfer_i : in std_logic; 59 | resp_expected_i : in std_logic; 60 | token_pid_i : in std_logic_vector(7 downto 0); 61 | token_dev_i : in std_logic_vector(6 downto 0); 62 | token_ep_i : in std_logic_vector(3 downto 0); 63 | data_len_i : in std_logic_vector(15 downto 0); 64 | data_idx_i : in std_logic; 65 | tx_data_i : in std_logic_vector(7 downto 0); 66 | utmi_txready_i : in std_logic; 67 | utmi_data_i : in std_logic_vector(7 downto 0); 68 | utmi_rxvalid_i : in std_logic; 69 | utmi_rxactive_i : in std_logic; 70 | 71 | ack_o : out std_logic; 72 | tx_pop_o : out std_logic; 73 | rx_data_o : out std_logic_vector(7 downto 0); 74 | rx_push_o : out std_logic; 75 | tx_done_o : out std_logic; 76 | rx_done_o : out std_logic; 77 | crc_err_o : out std_logic; 78 | timeout_o : out std_logic; 79 | response_o : out std_logic_vector(7 downto 0); 80 | rx_count_o : out std_logic_vector(15 downto 0); 81 | idle_o : out std_logic; 82 | utmi_linectrl_o : out std_logic; 83 | utmi_data_o : out std_logic_vector(7 downto 0); 84 | utmi_txvalid_o : out std_logic 85 | ); 86 | end component; 87 | 88 | signal reverse_token_dev_i : std_logic_vector(6 downto 0); 89 | signal reverse_token_ep_i : std_logic_vector(3 downto 0); 90 | 91 | begin 92 | 93 | reverse_token_dev_i <= 94 | token_dev_i(0) & token_dev_i(1) & token_dev_i(2) & token_dev_i(3) & 95 | token_dev_i(4) & token_dev_i(5) & token_dev_i(6); 96 | 97 | reverse_token_ep_i <= 98 | token_ep_i(0) & token_ep_i(1) & token_ep_i(2) & token_ep_i(3); 99 | 100 | usbh_sie_verilog_inst: usbh_sie 101 | port map ( 102 | clk_i => clk_i, 103 | rst_i => rst_i, 104 | start_i => start_i, 105 | in_transfer_i => in_transfer_i, 106 | sof_transfer_i => sof_transfer_i, 107 | resp_expected_i => resp_expected_i, 108 | token_pid_i => token_pid_i, 109 | token_dev_i => reverse_token_dev_i, 110 | token_ep_i => reverse_token_ep_i, 111 | data_len_i => data_len_i, 112 | data_idx_i => data_idx_i, 113 | tx_data_i => tx_data_i, 114 | utmi_txready_i => utmi_txready_i, 115 | utmi_data_i => utmi_data_i, 116 | utmi_rxvalid_i => utmi_rxvalid_i, 117 | utmi_rxactive_i => utmi_rxactive_i, 118 | 119 | ack_o => ack_o, 120 | tx_pop_o => tx_pop_o, 121 | rx_data_o => rx_data_o, 122 | rx_push_o => rx_push_o, 123 | tx_done_o => tx_done_o, 124 | rx_done_o => rx_done_o, 125 | crc_err_o => crc_err_o, 126 | timeout_o => timeout_o, 127 | response_o => response_o, 128 | rx_count_o => rx_count_o, 129 | idle_o => idle_o, 130 | utmi_linectrl_o => utmi_linectrl_o, 131 | utmi_data_o => utmi_data_o, 132 | utmi_txvalid_o => utmi_txvalid_o 133 | ); 134 | end syn; 135 | -------------------------------------------------------------------------------- /slot6.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emard/apple2fpga/0ad84ae2f9d9d5cc0e8db6128d630a9e16cdd6de/slot6.rom -------------------------------------------------------------------------------- /timing_generator.vhd: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | -- 3 | -- Apple ][ Timing logic 4 | -- 5 | -- Stephen A. Edwards, sedwards@cs.columbia.edu 6 | -- 7 | -- Taken more-or-less verbatim from the schematics in the 8 | -- Apple ][ reference manual 9 | -- 10 | -- This takes a 14.31818 MHz master clock and divides it down to generate 11 | -- the various lower-frequency signals (e.g., 7M, phase 0, colorburst) 12 | -- as well as horizontal and vertical blanking and sync signals for the video 13 | -- and the video addresses. 14 | -- 15 | ------------------------------------------------------------------------------- 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | use ieee.numeric_std.all; 20 | 21 | entity timing_generator is 22 | 23 | port ( 24 | CLK_14M : in std_logic; -- 14.31818 MHz master clock 25 | CLK_7M : buffer std_logic := '0'; 26 | Q3 : buffer std_logic := '0'; -- 2 MHz signal in phase with PHI0 27 | RAS_N : buffer std_logic := '0'; 28 | CAS_N : buffer std_logic := '0'; 29 | AX : buffer std_logic := '0'; 30 | PHI0 : buffer std_logic := '0'; -- 1.0 MHz processor clock 31 | PRE_PHI0 : buffer std_logic := '0'; -- One 14M cycle before 32 | COLOR_REF : buffer std_logic := '0'; -- 3.579545 MHz colorburst 33 | 34 | TEXT_MODE : in std_logic; 35 | PAGE2 : in std_logic; 36 | HIRES : in std_logic; 37 | 38 | VIDEO_ADDRESS : out unsigned(15 downto 0); 39 | H0 : out std_logic; 40 | VA : out std_logic; -- Character row address 41 | VB : out std_logic; 42 | VC : out std_logic; 43 | V2 : out std_logic; 44 | V4 : out std_logic; 45 | HBL : buffer std_logic; -- Horizontal blanking 46 | VBL : buffer std_logic; -- Vertical blanking 47 | BLANK : out std_logic; -- Composite blanking 48 | LDPS_N : out std_logic; 49 | LD194 : out std_logic 50 | ); 51 | 52 | end timing_generator; 53 | 54 | architecture rtl of timing_generator is 55 | 56 | signal H : unsigned(6 downto 0) := "0000000"; 57 | signal V : unsigned(8 downto 0) := "011111010"; 58 | signal COLOR_DELAY_N : std_logic; 59 | 60 | begin 61 | 62 | -- To generate the once-a-line hiccup: D1 pin 6 63 | COLOR_DELAY_N <= 64 | not (not COLOR_REF and (not AX and not CAS_N) and PHI0 and not H(6)); 65 | 66 | -- The DRAM signal generator 67 | C2_74S195: process (CLK_14M) 68 | begin 69 | if rising_edge(CLK_14M) then 70 | if Q3 = '1' then -- shift 71 | (Q3, CAS_N, AX, RAS_N) <= 72 | unsigned'(CAS_N, AX, RAS_N, '0'); 73 | else -- load 74 | (Q3, CAS_N, AX, RAS_N) <= 75 | unsigned'(RAS_N, AX, COLOR_DELAY_N, AX); 76 | end if; 77 | end if; 78 | end process; 79 | 80 | -- The main clock signal generator 81 | B1_74S175 : process (CLK_14M) 82 | begin 83 | if rising_edge(CLK_14M) then 84 | COLOR_REF <= CLK_7M xor COLOR_REF; 85 | CLK_7M <= not CLK_7M; 86 | PHI0 <= PRE_PHI0; 87 | if AX = '1' then 88 | PRE_PHI0 <= not (Q3 xor PHI0); -- B1 pin 10 89 | end if; 90 | end if; 91 | end process; 92 | 93 | LDPS_N <= not (PHI0 and not AX and not CAS_N); 94 | LD194 <= not (PHI0 and not AX and not CAS_N and not CLK_7M); 95 | 96 | -- Four four-bit presettable binary counters 97 | -- Seven-bit horizontal counter counts 0, 40, 41, ..., 7F (65 states) 98 | -- Nine-bit vertical counter counts $FA .. $1FF (262 states) 99 | D11D12D13D14_74LS161 : process (CLK_14M) 100 | begin 101 | if rising_edge(CLK_14M) then 102 | -- True the cycle before the rising edge of LDPS_N: emulates 103 | -- the effects of using LDPS_N as the clock for the video counters 104 | if (PHI0 and not AX and ((Q3 and RAS_N) or 105 | (not Q3 and COLOR_DELAY_N))) = '1' then 106 | if H(6) = '0' then H <= "1000000"; 107 | else 108 | H <= H + 1; 109 | if H = "1111111" then 110 | V <= V + 1; 111 | if V = "111111111" then V <= "011111010"; end if; 112 | end if; 113 | end if; 114 | end if; 115 | end if; 116 | 117 | end process; 118 | 119 | H0 <= H(0); 120 | VA <= V(0); 121 | VB <= V(1); 122 | VC <= V(2); 123 | V2 <= V(5); 124 | V4 <= V(7); 125 | 126 | HBL <= not (H(5) or (H(3) and H(4))); 127 | VBL <= V(6) and V(7); 128 | 129 | BLANK <= HBL or VBL; 130 | 131 | -- V_SYNC <= VBL and V(5) and not V(4) and not V(3) and 132 | -- not V(2) and (H(4) or H(3) or H(5)); 133 | -- H_SYNC <= HBL and H(3) and not H(2); 134 | 135 | -- SYNC <= not (V_SYNC or H_SYNC); 136 | -- COLOR_BURST <= HBL and H(2) and H(3) and (COLOR_REF or TEXT_MODE); 137 | 138 | -- Video address calculation 139 | VIDEO_ADDRESS(2 downto 0) <= H(2 downto 0); 140 | VIDEO_ADDRESS(6 downto 3) <= (not H(5) & V(6) & H(4) & H(3)) + 141 | ( V(7) & not H(5) & V(7) & '1') + 142 | ( "000" & V(6)); 143 | VIDEO_ADDRESS(9 downto 7) <= V(5 downto 3); 144 | VIDEO_ADDRESS(14 downto 10) <= 145 | ( "00" & HBL & PAGE2 & not PAGE2) when HIRES = '0' else 146 | (PAGE2 & not PAGE2 & V(2 downto 0)); 147 | 148 | VIDEO_ADDRESS(15) <= '0'; 149 | 150 | end rtl; 151 | -------------------------------------------------------------------------------- /timing_testbench.vhd: -------------------------------------------------------------------------------- 1 | library ieee; 2 | use ieee.std_logic_1164.all; 3 | use ieee.numeric_std.all; 4 | 5 | entity timing_testbench is 6 | 7 | end timing_testbench; 8 | 9 | architecture behavioral of timing_testbench is 10 | 11 | signal CLK_14M : std_logic := '0'; 12 | 13 | begin 14 | 15 | uut : entity work.timing_generator 16 | port map ( 17 | CLK_14M => CLK_14M, 18 | TEXT_MODE => '1', 19 | PAGE2 => '0', 20 | HIRES => '1' 21 | ); 22 | 23 | CLK_14M <= not CLK_14M after 34.920639355 ns; 24 | 25 | end behavioral; 26 | -------------------------------------------------------------------------------- /wm8731_audio.vhd: -------------------------------------------------------------------------------- 1 | library ieee; 2 | use ieee.std_logic_1164.all; 3 | use ieee.numeric_std.all; 4 | 5 | entity wm8731_audio is 6 | port ( 7 | clk : in std_logic; -- Audio CODEC Chip Clock AUD_XCK (18.43 MHz) 8 | reset : in std_logic; 9 | audio_request : out std_logic; -- Audio controller request new data 10 | data : in unsigned(15 downto 0); 11 | 12 | -- Audio interface signals 13 | AUD_ADCLRCK : out std_logic; -- Audio CODEC ADC LR Clock 14 | AUD_ADCDAT : in std_logic; -- Audio CODEC ADC Data 15 | AUD_DACLRCK : out std_logic; -- Audio CODEC DAC LR Clock 16 | AUD_DACDAT : out std_logic; -- Audio CODEC DAC Data 17 | AUD_BCLK : inout std_logic -- Audio CODEC Bit-Stream Clock 18 | ); 19 | end wm8731_audio; 20 | 21 | architecture rtl of wm8731_audio is 22 | 23 | signal lrck : std_logic; 24 | signal bclk : std_logic; 25 | signal xck : std_logic; 26 | 27 | signal lrck_divider : unsigned(7 downto 0); 28 | signal bclk_divider : unsigned(3 downto 0); 29 | 30 | signal set_bclk : std_logic; 31 | signal set_lrck : std_logic; 32 | signal clr_bclk : std_logic; 33 | signal lrck_lat : std_logic; 34 | 35 | signal shift_out : unsigned(15 downto 0); 36 | 37 | begin 38 | 39 | -- LRCK divider 40 | -- Audio chip main clock is 18.432MHz / Sample rate 48KHz 41 | -- Divider is 18.432 MHz / 48KHz = 192 (X"C0") 42 | -- Left justify mode set by I2C controller 43 | 44 | process (clk) 45 | begin 46 | if rising_edge(clk) then 47 | if reset = '1' then 48 | lrck_divider <= (others => '0'); 49 | elsif lrck_divider = X"BF" then -- "C0" minus 1 50 | lrck_divider <= X"00"; 51 | else 52 | lrck_divider <= lrck_divider + 1; 53 | end if; 54 | end if; 55 | end process; 56 | 57 | process (clk) 58 | begin 59 | if rising_edge(clk) then 60 | if reset = '1' then 61 | bclk_divider <= (others => '0'); 62 | elsif bclk_divider = X"B" or set_lrck = '1' then 63 | bclk_divider <= X"0"; 64 | else 65 | bclk_divider <= bclk_divider + 1; 66 | end if; 67 | end if; 68 | end process; 69 | 70 | set_lrck <= '1' when lrck_divider = X"BF" else '0'; 71 | 72 | process (clk) 73 | begin 74 | if rising_edge(clk) then 75 | if reset = '1' then 76 | lrck <= '0'; 77 | elsif set_lrck = '1' then 78 | lrck <= not lrck; 79 | end if; 80 | end if; 81 | end process; 82 | 83 | -- BCLK divider 84 | set_bclk <= '1' when bclk_divider(3 downto 0) = "0101" else '0'; 85 | clr_bclk <= '1' when bclk_divider(3 downto 0) = "1011" else '0'; 86 | 87 | process (clk) 88 | begin 89 | if rising_edge(clk) then 90 | if reset = '1' then 91 | bclk <= '0'; 92 | elsif set_lrck = '1' or clr_bclk = '1' then 93 | bclk <= '0'; 94 | elsif set_bclk = '1' then 95 | bclk <= '1'; 96 | end if; 97 | end if; 98 | end process; 99 | 100 | -- Audio data shift output 101 | process (clk) 102 | begin 103 | if rising_edge(clk) then 104 | if reset = '1' then 105 | shift_out <= (others => '0'); 106 | elsif set_lrck = '1' then 107 | shift_out <= data; 108 | elsif clr_bclk = '1' then 109 | shift_out <= shift_out (14 downto 0) & '0'; 110 | end if; 111 | end if; 112 | end process; 113 | 114 | -- Audio outputs 115 | 116 | AUD_ADCLRCK <= lrck; 117 | AUD_DACLRCK <= lrck; 118 | AUD_DACDAT <= shift_out(15); 119 | AUD_BCLK <= bclk; 120 | 121 | process(clk) 122 | begin 123 | if rising_edge(clk) then 124 | lrck_lat <= lrck; 125 | end if; 126 | end process; 127 | 128 | process (clk) 129 | begin 130 | if rising_edge(clk) then 131 | if lrck_lat = '1' and lrck = '0' then 132 | audio_request <= '1'; 133 | else 134 | audio_request <= '0'; 135 | end if; 136 | end if; 137 | end process; 138 | 139 | end architecture; 140 | 141 | 142 | --------------------------------------------------------------------------------