├── .github └── workflows │ └── c-cpp.yml ├── .gitignore ├── .gitmodules ├── CART ├── AmstradPlus.f4.cpr ├── Burnin Rubber (UK) (1990) [Original] [CARTOUCHE].cpr └── crtc3_projo.cpr ├── Makefile ├── README.md ├── Rules.mk ├── build_all.sh ├── build_circle.sh ├── build_pi3.sh ├── build_pi4.sh ├── config ├── gamecontrollerdb.txt ├── res ├── 101_keyboard ├── Credits.txt ├── PressStart2P.bmp ├── PressStart2P.c ├── PressStart2P.ttf ├── button_1.h ├── button_1.png ├── coolspot.cpp ├── coolspot.h ├── coolspot_alpha.png ├── lemblue.png ├── lemblue2.bmp ├── lemblue2.png ├── lemblue3.jpg └── lemblue3.png ├── src ├── Cartridge.h ├── ConfigurationManager.cpp ├── ConfigurationManager.h ├── DisplayPi.cpp ├── DisplayPi.h ├── KeyboardPi.cpp ├── KeyboardPi.h ├── ScreenMenu.cpp ├── ScreenMenu.h ├── SoundPi.cpp ├── SoundPi.h ├── SugarPiSetup.cpp ├── SugarPiSetup.h ├── Windows.cpp ├── Windows.h ├── emulation.cpp ├── emulation.h ├── kernel.cpp ├── kernel.h ├── log.cpp ├── log.h └── main.cpp └── sugarpi-action ├── Dockerfile ├── action.yml └── entrypoint_docker.sh /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: [ master, develop ] 6 | pull_request: 7 | branches: [ master, develop ] 8 | workflow_call: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout code 16 | uses: actions/checkout@v3 17 | 18 | - name: Checkout submodules 19 | run: git submodule update --init --recursive 20 | 21 | - name: Set circle as writable 22 | run: | 23 | sudo chown -R $USER:$USER $(pwd) 24 | ls circle 25 | ls circle/boot 26 | shell: bash 27 | working-directory: . 28 | 29 | - name: Prepare SD Card 30 | run: | 31 | mkdir sdcard 32 | shell: bash 33 | working-directory: . 34 | 35 | - name: Build Pi 36 | uses: ./sugarpi-action 37 | id: buildpi 38 | 39 | - uses: actions/upload-artifact@v2 40 | with: 41 | name: SugarPi 42 | path: sdcard/* 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *l.o 2 | *.elf 3 | *.img 4 | /main.o 5 | **.o 6 | /.vs/ProjectSettings.json 7 | /.vs/** 8 | /kernel8.lst 9 | /kernel8.map 10 | /kernel7** 11 | pi2** 12 | /Config.mk 13 | /pi/** 14 | /sdcard/** 15 | 16 | /.vscode/settings.json 17 | /.vscode/** 18 | /build/** 19 | all 20 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "CPCCore"] 2 | path = CPCCore 3 | url = https://github.com/Tom1975/CPCCore.git 4 | [submodule "circle"] 5 | path = circle 6 | url = https://github.com/Tom1975/circle 7 | branch = master 8 | -------------------------------------------------------------------------------- /CART/AmstradPlus.f4.cpr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tom1975/SugarPi/6ce5d8e77a2ae250e894a991c5ce759379700f69/CART/AmstradPlus.f4.cpr -------------------------------------------------------------------------------- /CART/Burnin Rubber (UK) (1990) [Original] [CARTOUCHE].cpr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tom1975/SugarPi/6ce5d8e77a2ae250e894a991c5ce759379700f69/CART/Burnin Rubber (UK) (1990) [Original] [CARTOUCHE].cpr -------------------------------------------------------------------------------- /CART/crtc3_projo.cpr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tom1975/SugarPi/6ce5d8e77a2ae250e894a991c5ce759379700f69/CART/crtc3_projo.cpr -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile 3 | # 4 | 5 | CIRCLEHOME = ./circle 6 | 7 | OBJS = src/main.o\ 8 | src/kernel.o\ 9 | src/SugarPiSetup.o\ 10 | src/ConfigurationManager.o\ 11 | src/DisplayPi.o\ 12 | src/emulation.o\ 13 | src/KeyboardPi.o\ 14 | src/log.o\ 15 | src/SoundPi.o\ 16 | src/ScreenMenu.o\ 17 | src/Windows.o\ 18 | res/coolspot.o\ 19 | CPCCore/CPCCoreEmu/Asic.o\ 20 | CPCCore/CPCCoreEmu/Bus.o\ 21 | CPCCore/CPCCoreEmu/CAPSFile.o\ 22 | CPCCore/CPCCoreEmu/ClockLine.o\ 23 | CPCCore/CPCCoreEmu/CRC.o\ 24 | CPCCore/CPCCoreEmu/CRTC.o \ 25 | CPCCore/CPCCoreEmu/CRTC_0.o \ 26 | CPCCore/CPCCoreEmu/CRTC_1.o \ 27 | CPCCore/CPCCoreEmu/CRTC_2.o \ 28 | CPCCore/CPCCoreEmu/CRTC_3_4.o \ 29 | CPCCore/CPCCoreEmu/DiskBuilder.o\ 30 | CPCCore/CPCCoreEmu/DiskContainer.o\ 31 | CPCCore/CPCCoreEmu/DiskGen.o\ 32 | CPCCore/CPCCoreEmu/DMA.o\ 33 | CPCCore/CPCCoreEmu/DskTypeManager.o\ 34 | CPCCore/CPCCoreEmu/FDC.o\ 35 | CPCCore/CPCCoreEmu/FileAccess.o\ 36 | CPCCore/CPCCoreEmu/FormatTypeCTRAW.o\ 37 | CPCCore/CPCCoreEmu/FormatTypeDSK.o\ 38 | CPCCore/CPCCoreEmu/FormatTypeEDSK.o\ 39 | CPCCore/CPCCoreEmu/FormatTypeHFE.o\ 40 | CPCCore/CPCCoreEmu/FormatTypeHFEv3.o\ 41 | CPCCore/CPCCoreEmu/FormatTypeIPF.o\ 42 | CPCCore/CPCCoreEmu/IDisk.o\ 43 | CPCCore/CPCCoreEmu/KeyboardHandler.o\ 44 | CPCCore/CPCCoreEmu/MediaManager.o\ 45 | CPCCore/CPCCoreEmu/Memoire.o\ 46 | CPCCore/CPCCoreEmu/Monitor.o \ 47 | CPCCore/CPCCoreEmu/Motherboard.o\ 48 | CPCCore/CPCCoreEmu/MultifaceII.o\ 49 | CPCCore/CPCCoreEmu/PPI.o \ 50 | CPCCore/CPCCoreEmu/PlayCity.o \ 51 | CPCCore/CPCCoreEmu/PrinterDefault.o\ 52 | CPCCore/CPCCoreEmu/PSG.o \ 53 | CPCCore/CPCCoreEmu/rand.o \ 54 | CPCCore/CPCCoreEmu/Sig.o \ 55 | CPCCore/CPCCoreEmu/simple_filesystem.o \ 56 | CPCCore/CPCCoreEmu/simple_math.o \ 57 | CPCCore/CPCCoreEmu/simple_regex.o \ 58 | CPCCore/CPCCoreEmu/simple_stdio.o \ 59 | CPCCore/CPCCoreEmu/simple_string.o \ 60 | CPCCore/CPCCoreEmu/Snapshot.o \ 61 | CPCCore/CPCCoreEmu/SoundMixer.o\ 62 | CPCCore/CPCCoreEmu/Tape.o\ 63 | CPCCore/CPCCoreEmu/VGA.o \ 64 | CPCCore/CPCCoreEmu/YMZ294.o\ 65 | CPCCore/CPCCoreEmu/Z80_Full.o\ 66 | CPCCore/CPCCoreEmu/Z80_Opcodes_fetch.o\ 67 | CPCCore/CPCCoreEmu/Z80_Opcodes_ior.o\ 68 | CPCCore/CPCCoreEmu/Z80_Opcodes_iow.o\ 69 | CPCCore/CPCCoreEmu/Z80_Opcodes_memr.o\ 70 | CPCCore/CPCCoreEmu/Z80_Opcodes_memw.o\ 71 | CPCCore/CPCCoreEmu/Z80_Opcodes_z80wait.o\ 72 | CPCCore/CPCCoreEmu/Z84C30.o\ 73 | 74 | 75 | EXTRACLEAN = $(OBJS) 76 | 77 | OPTIMIZE = -O3 78 | 79 | LIBS = $(CIRCLEHOME)/lib/libcircle.a \ 80 | $(CIRCLEHOME)/lib/fs/fat/libfatfs.a \ 81 | $(CIRCLEHOME)/lib/fs/libfs.a \ 82 | $(CIRCLEHOME)/lib/usb/libusb.a \ 83 | $(CIRCLEHOME)/lib/input/libinput.a \ 84 | $(CIRCLEHOME)/lib/sched/libsched.a \ 85 | $(CIRCLEHOME)/addon/linux/liblinuxemu.a \ 86 | $(CIRCLEHOME)/addon/SDCard/libsdcard.a\ 87 | $(CIRCLEHOME)/addon/fatfs/libfatfs.a\ 88 | $(CIRCLEHOME)/addon/vc4/sound/libvchiqsound.a\ 89 | $(CIRCLEHOME)/addon/vc4/vchiq/libvchiq.a 90 | 91 | 92 | include circle/Rules.mk 93 | 94 | CFLAGS += -DMINIMUM_DEPENDENCIES -DUSE_VCHIQ_SOUND -DNO_MULTITHREAD -I. -Isrc -ICPCCore/zlib_pi -DNOFILTER -DNOZLIB -DNO_RAW_FORMAT -I$(CIRCLEHOME)/addon -DLOG_MIXER -DLOGFDC 95 | CPPFLAGS += -DMINIMUM_DEPENDENCIES -DUSE_VCHIQ_SOUND -DNO_MULTITHREAD -I. -Isrc -ICPCCore/zlib_pi -DNOFILTER -DNOZLIB -DNO_RAW_FORMAT -I$(CIRCLEHOME)/addon -DLOG_MIXER -DLOGFDC -std=c++1z 96 | 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SugarPi 2 | 3 | [![C/C++ CI](https://github.com/Tom1975/SugarPi/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/Tom1975/SugarPi/actions/workflows/c-cpp.yml) 4 | 5 | SugarPi is an implementation of Sugarbox for bare-metal raspberry Pi. 6 | By now, the emulator is working correctly on Raspberry Pi3(+), and more or less on Raspberry Pi4. 7 | I didn't have a chance to test on Pi2 or 400. 8 | 9 | As a first version, it emulatse an Amstrad 6128+ without disk drive nor keyboard (so , a kind of Gx4000 with 128 KB). 10 | 11 | It uses the great [Circle++ library](https://github.com/rsta2/circle) as a pseudo-os basis. 12 | 13 | # How to use 14 | 15 | ## Start 16 | - Unzip the zip file to an empty SD card. 17 | - Fill the CART folder with you favorite CPR files 18 | - Insert the SD card in your Raspberry Pi3 19 | - Power it : The Amstrad will be immediately displayed. 20 | 21 | ## Commands 22 | You should have an USB joypad plugged. I tested it with my XBox pad successfully. 23 | A and X button act as button 1 and 2 from the Amstrad pad. 24 | Directional pad is used for controler. 25 | Start is pause, and Select as a way to go into SugarPi settings. 26 | 27 | ## Changing settings 28 | From anytime, push Select button : it will bring the settings menu on the screen. 29 | From here, you can : 30 | - Select a cartridge to load : Go into "select cartridge" menu. It will display the CPR files from your CART folder. Select one, then press A : It will load the cartridge, and reset the computer. Note : On the next SugarPi power on, this cartridge will be the default one. 31 | - Select the synchronisation type : 32 | - Frame sync will synchronise with the frame. It means that the synchronisation will be smooth, but the timings (and the sound) will be (almost) correct, as long as you are plugged on a 50hz monitor or TV. 33 | - If frame sync is unchecked, synchronisation will be done on sound : Sound should be more precise, but you can experience some tearing on the display. 34 | 35 | ## Supported hardware 36 | SugarPi was developped on the Raspberry Pi3B+. It has been adapted to Raspeberry Pi4. 37 | I didn't test it on Raspberry Pi400, but as Circle++ doesn't support it, it may not work on it. 38 | 39 | ## Gamepad 40 | Gamepads are supported through gamecontrollerdb.txt. 41 | If your hardware is not supported yet, add a line in the 'Linux' section (see [Gamecontrollerdb.txt](https://github.com/gabomdq/SDL_GameControllerDB) for more information). 42 | In the future, I'll try to stick to the gamecontrollerdb.txt repository. 43 | 44 | ## Known issues 45 | - Some gamepads are not properly supported on Raspberry Pi4 (XBox 360, ...). 46 | - Raspberry Pi4 may experience slowdowns 47 | - 48 | ## Credits 49 | - [Circle++ library](https://github.com/rsta2/circle) for bare-metal projects on Raspberry Pi 50 | - [Gamecontrollerdb.txt](https://github.com/gabomdq/SDL_GameControllerDB) for gamepad mapping support 51 | - [Sugarbox Core library](https://github.com/Tom1975/CPCCore) for Amstrad CPC core emulation 52 | -------------------------------------------------------------------------------- /Rules.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Rules.mk 3 | # 4 | # Circle - A C++ bare metal environment for Raspberry Pi 5 | # Copyright (C) 2014-2020 R. Stange 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | CIRCLEHOME ?= .. 22 | 23 | -include $(CIRCLEHOME)/Config.mk 24 | -include $(CIRCLEHOME)/Config2.mk # is not overwritten by "configure" 25 | 26 | AARCH ?= 32 27 | RASPPI ?= 1 28 | PREFIX ?= arm-none-eabi- 29 | PREFIX64 ?= aarch64-none-elf- 30 | POSTFIX ?= 31 | 32 | # see: doc/stdlib-support.txt 33 | STDLIB_SUPPORT ?= 1 34 | 35 | # set this to 0 to globally disable dependency checking 36 | CHECK_DEPS ?= 1 37 | 38 | # set this to "softfp" if you want to link specific libraries 39 | FLOAT_ABI ?= hard 40 | 41 | # set this to 1 to enable garbage collection on sections, may cause side effects 42 | GC_SECTIONS ?= 0 43 | 44 | CC = $(PREFIX)gcc$(POSTFIX) 45 | CPP = $(PREFIX)g++$(POSTFIX) 46 | AS = $(CC) 47 | LD = $(PREFIX)ld$(POSTFIX) 48 | AR = $(PREFIX)ar$(POSTFIX) 49 | 50 | ifeq ($(strip $(AARCH)),32) 51 | ifeq ($(strip $(RASPPI)),1) 52 | ARCH ?= -DAARCH=32 -mcpu=arm1176jzf-s -marm -mfpu=vfp -mfloat-abi=$(FLOAT_ABI) 53 | TARGET ?= kernel 54 | else ifeq ($(strip $(RASPPI)),2) 55 | ARCH ?= -DAARCH=32 -mcpu=cortex-a7 -marm -mfpu=neon-vfpv4 -mfloat-abi=$(FLOAT_ABI) 56 | TARGET ?= kernel7 57 | else ifeq ($(strip $(RASPPI)),3) 58 | ARCH ?= -DAARCH=32 -mcpu=cortex-a53 -marm -mfpu=neon-fp-armv8 -mfloat-abi=$(FLOAT_ABI) 59 | TARGET ?= kernel8-32 60 | else ifeq ($(strip $(RASPPI)),4) 61 | ARCH ?= -DAARCH=32 -mcpu=cortex-a72 -marm -mfpu=neon-fp-armv8 -mfloat-abi=$(FLOAT_ABI) 62 | TARGET ?= kernel7l 63 | else 64 | $(error RASPPI must be set to 1, 2, 3 or 4) 65 | endif 66 | LOADADDR = 0x8000 67 | else ifeq ($(strip $(AARCH)),64) 68 | ifeq ($(strip $(RASPPI)),3) 69 | ARCH ?= -DAARCH=64 -mcpu=cortex-a53 -mlittle-endian -mcmodel=small 70 | TARGET ?= kernel8 71 | else ifeq ($(strip $(RASPPI)),4) 72 | ARCH ?= -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -mcmodel=small 73 | TARGET ?= kernel8-rpi4 74 | else 75 | $(error RASPPI must be set to 3 or 4) 76 | endif 77 | PREFIX = $(PREFIX64) 78 | LOADADDR = 0x80000 79 | else 80 | $(error AARCH must be set to 32 or 64) 81 | endif 82 | 83 | ifneq ($(strip $(STDLIB_SUPPORT)),0) 84 | MAKE_VERSION_MAJOR := $(firstword $(subst ., ,$(MAKE_VERSION))) 85 | ifneq ($(filter 0 1 2 3,$(MAKE_VERSION_MAJOR)),) 86 | $(error STDLIB_SUPPORT > 0 requires GNU make 4.0 or newer) 87 | endif 88 | endif 89 | 90 | ifeq ($(strip $(STDLIB_SUPPORT)),3) 91 | LIBSTDCPP != $(CPP) $(ARCH) -print-file-name=libstdc++.a 92 | EXTRALIBS += $(LIBSTDCPP) 93 | LIBGCC_EH != $(CPP) $(ARCH) -print-file-name=libgcc_eh.a 94 | ifneq ($(strip $(LIBGCC_EH)),libgcc_eh.a) 95 | EXTRALIBS += $(LIBGCC_EH) 96 | endif 97 | ifeq ($(strip $(AARCH)),64) 98 | CRTBEGIN != $(CPP) $(ARCH) -print-file-name=crtbegin.o 99 | CRTEND != $(CPP) $(ARCH) -print-file-name=crtend.o 100 | endif 101 | else 102 | CPPFLAGS += -fno-exceptions -fno-rtti -nostdinc++ 103 | endif 104 | 105 | ifeq ($(strip $(STDLIB_SUPPORT)),0) 106 | CFLAGS += -nostdinc 107 | else 108 | LIBGCC != $(CPP) $(ARCH) -print-file-name=libgcc.a 109 | EXTRALIBS += $(LIBGCC) 110 | endif 111 | 112 | ifeq ($(strip $(STDLIB_SUPPORT)),1) 113 | LIBM != $(CPP) $(ARCH) -print-file-name=libm.a 114 | ifneq ($(strip $(LIBM)),libm.a) 115 | EXTRALIBS += $(LIBM) 116 | endif 117 | endif 118 | 119 | ifeq ($(strip $(GC_SECTIONS)),1) 120 | CFLAGS += -ffunction-sections -fdata-sections 121 | LDFLAGS += --gc-sections 122 | endif 123 | 124 | OPTIMIZE ?= -O2 125 | 126 | INCLUDE += -I $(CIRCLEHOME)/include -I $(CIRCLEHOME)/addon -I $(CIRCLEHOME)/app/lib \ 127 | -I $(CIRCLEHOME)/addon/vc4 -I $(CIRCLEHOME)/addon/vc4/interface/khronos/include 128 | DEFINE += -D__circle__ -DRASPPI=$(RASPPI) -DSTDLIB_SUPPORT=$(STDLIB_SUPPORT) \ 129 | -D__VCCOREVER__=0x04000000 -U__unix__ -U__linux__ #-DNDEBUG 130 | 131 | AFLAGS += $(ARCH) $(DEFINE) $(INCLUDE) $(OPTIMIZE) 132 | CFLAGS += $(ARCH) -Wall -fsigned-char -ffreestanding $(DEFINE) $(INCLUDE) $(OPTIMIZE) -g 133 | CPPFLAGS+= $(CFLAGS) -std=c++14 -Wno-aligned-new 134 | LDFLAGS += --section-start=.init=$(LOADADDR) 135 | 136 | ifeq ($(strip $(CHECK_DEPS)),1) 137 | DEPS = $(OBJS:.o=.d) 138 | endif 139 | 140 | %.o: %.S 141 | @echo " AS $@" 142 | @$(AS) $(AFLAGS) -c -o $@ $< 143 | 144 | %.o: %.c 145 | @echo " CC $@" 146 | @$(CC) $(CFLAGS) -std=gnu99 -c -o $@ $< 147 | 148 | %.o: %.cpp 149 | @echo " CPP $@" 150 | @$(CPP) $(CPPFLAGS) -c -o $@ $< 151 | 152 | %.d: %.S 153 | @$(AS) $(AFLAGS) -M -MG -MT $*.o -MT $@ -MF $@ $< 154 | 155 | %.d: %.c 156 | @$(CC) $(CFLAGS) -M -MG -MT $*.o -MT $@ -MF $@ $< 157 | 158 | %.d: %.cpp 159 | @$(CPP) $(CPPFLAGS) -M -MG -MT $*.o -MT $@ -MF $@ $< 160 | 161 | $(TARGET).img: $(OBJS) $(LIBS) $(CIRCLEHOME)/circle.ld 162 | @echo " LD $(TARGET).elf" 163 | @$(LD) -o $(TARGET).elf -Map $(TARGET).map $(LDFLAGS) \ 164 | -T $(CIRCLEHOME)/circle.ld $(CRTBEGIN) $(OBJS) \ 165 | --start-group $(LIBS) $(EXTRALIBS) --end-group $(CRTEND) 166 | @echo " DUMP $(TARGET).lst" 167 | @$(PREFIX)objdump$(POSTFIX) -d $(TARGET).elf | $(PREFIX)c++filt$(POSTFIX) > $(TARGET).lst 168 | @echo " COPY $(TARGET).img" 169 | @$(PREFIX)objcopy$(POSTFIX) $(TARGET).elf -O binary $(TARGET).img 170 | @echo -n " WC $(TARGET).img => " 171 | @wc -c < $(TARGET).img 172 | 173 | clean: 174 | rm -f *.d *.o *.a *.elf *.lst *.img *.hex *.cir *.map *~ $(EXTRACLEAN) 175 | 176 | ifneq ($(strip $(SDCARD)),) 177 | install: $(TARGET).img 178 | cp $(TARGET).img $(SDCARD) 179 | sync 180 | endif 181 | 182 | ifneq ($(strip $(TFTPHOST)),) 183 | tftpboot: $(TARGET).img 184 | tftp -m binary $(TFTPHOST) -c put $(TARGET).img 185 | endif 186 | 187 | # 188 | # Eclipse support 189 | # 190 | 191 | SERIALPORT ?= /dev/ttyUSB0 192 | USERBAUD ?= 115200 193 | FLASHBAUD ?= 115200 194 | REBOOTMAGIC ?= 195 | 196 | $(TARGET).hex: $(TARGET).img 197 | @echo " COPY $(TARGET).hex" 198 | @$(PREFIX)objcopy $(TARGET).elf -O ihex $(TARGET).hex 199 | 200 | flash: $(TARGET).hex 201 | ifneq ($(strip $(REBOOTMAGIC)),) 202 | python3 $(CIRCLEHOME)/tools/reboottool.py $(REBOOTMAGIC) $(SERIALPORT) $(USERBAUD) 203 | endif 204 | python3 $(CIRCLEHOME)/tools/flasher.py $(TARGET).hex $(SERIALPORT) $(FLASHBAUD) 205 | 206 | monitor: 207 | putty -serial $(SERIALPORT) -sercfg $(USERBAUD) 208 | -------------------------------------------------------------------------------- /build_all.sh: -------------------------------------------------------------------------------- 1 | ./build_pi3.sh 2 | ./build_pi4.sh 3 | 4 | echo "*** copy boot to sdcard ***" 5 | cd circle/boot 6 | make 7 | cd ../.. 8 | 9 | cp circle/boot/bootcode.bin sdcard 10 | cp circle/boot/start.elf sdcard 11 | cp circle/boot/start4.elf sdcard 12 | cp circle/boot/fixup.dat sdcard 13 | cp circle/boot/fixup4.dat sdcard 14 | cp circle/boot/bcm2711-rpi-4-b.dtb sdcard 15 | cp circle/boot/LICENCE.broadcom sdcard 16 | cp circle/boot/COPYING.linux sdcard 17 | cp circle/boot/config64.txt sdcard/config.txt 18 | 19 | echo "*** copy default CART sdcard ***" 20 | 21 | if [ ! -d "sdcard/CART" ]; then 22 | mkdir sdcard/CART 23 | fi 24 | 25 | echo "*** base config creation ***" 26 | if [ ! -d "sdcard/Config" ]; then 27 | mkdir sdcard/Config 28 | fi 29 | 30 | if [ ! -d "sdcard/LAYOUT" ]; then 31 | mkdir sdcard/LAYOUT 32 | fi 33 | 34 | echo "[SETUP]" > config 35 | echo "sync=frame" >> config 36 | echo "cart=SD:/CART/crtc3_projo.cpr" >> config 37 | 38 | 39 | cp res/101_keyboard sdcard/LAYOUT 40 | cp config sdcard/Config 41 | cp CART/* sdcard/CART 42 | cp gamecontrollerdb.txt sdcard/Config/gamecontrollerdb.txt 43 | -------------------------------------------------------------------------------- /build_circle.sh: -------------------------------------------------------------------------------- 1 | echo "*** Build Circle ***" 2 | 3 | cp Rules.mk circle 4 | cd circle 5 | 6 | ./makeall clean 7 | ./makeall --nosample 8 | 9 | cd addon/linux 10 | make clean 11 | make 12 | 13 | cd ../SDCard 14 | make clean 15 | make 16 | 17 | cd ../fatfs 18 | make clean 19 | make 20 | 21 | cd ../vc4/sound 22 | make clean 23 | make 24 | 25 | cd ../vchiq 26 | make clean 27 | make 28 | 29 | cd ../../boot 30 | make clean 31 | make 32 | 33 | echo "*** End of Circle build ***" 34 | -------------------------------------------------------------------------------- /build_pi3.sh: -------------------------------------------------------------------------------- 1 | echo "*** Build target for Raspberry Pi 3 ***" 2 | 3 | # Set arch 4 | echo AARCH = 64 > Config.mk 5 | echo RASPPI = 3 >> Config.mk 6 | echo PREFIX64 = aarch64-none-elf- >> Config.mk 7 | echo STDLIB_SUPPORT = 1 >> Config.mk 8 | echo DEFINE = -DARM_ALLOW_MULTI_CORE >> Config.mk 9 | echo CHECK_DEPS = 0 >> Config.mk 10 | 11 | cp Config.mk circle/. 12 | ./build_circle.sh 13 | 14 | # check output dir 15 | make clean 16 | make 17 | 18 | # copy to target directory 19 | if [ ! -d "sdcard" ]; then 20 | mkdir sdcard 21 | fi 22 | cp kernel8.img sdcard/. 23 | 24 | echo "*** End ***" 25 | -------------------------------------------------------------------------------- /build_pi4.sh: -------------------------------------------------------------------------------- 1 | echo "*** Build target for Raspberry Pi 4 ***" 2 | 3 | # Set arch 4 | echo AARCH = 64 > Config.mk 5 | echo RASPPI = 4 >> Config.mk 6 | echo PREFIX64 = aarch64-none-elf- >> Config.mk 7 | echo STDLIB_SUPPORT = 1 >> Config.mk 8 | echo DEFINE = -DARM_ALLOW_MULTI_CORE >> Config.mk 9 | echo CHECK_DEPS = 0 >> Config.mk 10 | 11 | cp Config.mk circle/. 12 | ./build_circle.sh 13 | 14 | # check output dir 15 | make clean 16 | make 17 | 18 | # copy to target directory 19 | if [ ! -d "sdcard" ]; then 20 | mkdir sdcard 21 | fi 22 | cp kernel8-rpi4.img sdcard/. 23 | 24 | echo "*** End ***" 25 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | [SETUP] 2 | sync=frame 3 | cart=SD:/CART/crtc3_projo.cpr 4 | -------------------------------------------------------------------------------- /res/101_keyboard: -------------------------------------------------------------------------------- 1 | # 2 | # 101 Enhanced Keyboard Layout. 3 | # 4 | # bit horizontal, from 0 -> 7 5 | # lines from top (0) to bottom (9) 6 | # Each value is the hexadecimal raw key code. 7 | # 8 | 9 | 52 4F 51 61 5E 5B 58 63 10 | 50 E2 5F 60 5D 59 5A 62 11 | 4C 30 28 32 5C E5 38 E0 12 | 2E 2D 2F 13 34 33 2E 37 13 | 27 26 12 0C 0F 0E 10 36 14 | 25 24 18 1C 0B 0D 11 2C 15 | 23 22 15 17 0A 09 05 19 16 | 21 20 08 1A 16 07 06 1B 17 | 1E 1F 29 14 2B 04 39 1D 18 | 00 00 00 00 00 00 00 2A 19 | -------------------------------------------------------------------------------- /res/Credits.txt: -------------------------------------------------------------------------------- 1 | - Bitmap button by Freepik.com 2 | #- Font by Joiro Hatagaya (JOIRO@HOTMAIL.COM / http://www.typesource.com/Presents/Hatagaya/Fonts.html -------------------------------------------------------------------------------- /res/PressStart2P.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tom1975/SugarPi/6ce5d8e77a2ae250e894a991c5ce759379700f69/res/PressStart2P.bmp -------------------------------------------------------------------------------- /res/PressStart2P.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tom1975/SugarPi/6ce5d8e77a2ae250e894a991c5ce759379700f69/res/PressStart2P.ttf -------------------------------------------------------------------------------- /res/button_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tom1975/SugarPi/6ce5d8e77a2ae250e894a991c5ce759379700f69/res/button_1.png -------------------------------------------------------------------------------- /res/coolspot.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class CoolspotFont 6 | { 7 | public: 8 | CoolspotFont(CLogger* logger); 9 | virtual ~CoolspotFont(); 10 | 11 | void CopyLetter(unsigned char c, int line, int* buffer); 12 | int GetLetterLength(unsigned char c); 13 | int GetLetterHeight(unsigned char c); 14 | 15 | //protected: 16 | int char_position_[256]; 17 | CLogger* logger_; 18 | }; -------------------------------------------------------------------------------- /res/coolspot_alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tom1975/SugarPi/6ce5d8e77a2ae250e894a991c5ce759379700f69/res/coolspot_alpha.png -------------------------------------------------------------------------------- /res/lemblue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tom1975/SugarPi/6ce5d8e77a2ae250e894a991c5ce759379700f69/res/lemblue.png -------------------------------------------------------------------------------- /res/lemblue2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tom1975/SugarPi/6ce5d8e77a2ae250e894a991c5ce759379700f69/res/lemblue2.bmp -------------------------------------------------------------------------------- /res/lemblue2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tom1975/SugarPi/6ce5d8e77a2ae250e894a991c5ce759379700f69/res/lemblue2.png -------------------------------------------------------------------------------- /res/lemblue3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tom1975/SugarPi/6ce5d8e77a2ae250e894a991c5ce759379700f69/res/lemblue3.jpg -------------------------------------------------------------------------------- /res/lemblue3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tom1975/SugarPi/6ce5d8e77a2ae250e894a991c5ce759379700f69/res/lemblue3.png -------------------------------------------------------------------------------- /src/ConfigurationManager.cpp: -------------------------------------------------------------------------------- 1 | #include "ConfigurationManager.h" 2 | 3 | #include 4 | #include 5 | 6 | CLogger* log_s = nullptr; 7 | 8 | ///////////////////////////////////////////////////////////// 9 | /// Helper functions 10 | ConfigurationManager::ConfigurationManager(CLogger* log) : logger_(log) 11 | { 12 | log_s = log; 13 | } 14 | 15 | ConfigurationManager::~ConfigurationManager() 16 | { 17 | // Clear everything 18 | Clear(); 19 | } 20 | 21 | void ConfigurationManager::Clear() 22 | { 23 | /* for (auto const& ent1 : config_file_) 24 | { 25 | // ent1.first is the first key 26 | for (auto const& ent2 : ent1) 27 | { 28 | // ent2.first is the second key 29 | // ent2.second is the data 30 | } 31 | }*/ 32 | config_file_.clear(); 33 | } 34 | 35 | unsigned int ConfigurationManager::getline ( const char* buffer, int size, std::string& out) 36 | { 37 | if ( size == 0) 38 | { 39 | return 0; 40 | } 41 | 42 | // looking for /n 43 | int offset = 0; 44 | while (buffer[offset] != 0x0A && buffer[offset] != 0x0D && offset < size) 45 | { 46 | offset++; 47 | } 48 | 49 | char* line = new char[offset+1]; 50 | memcpy ( line, buffer, offset); 51 | line[offset] = '\0'; 52 | out = std::string(line); 53 | delete []line; 54 | return (offset == size)?offset:offset+1; 55 | } 56 | 57 | void ConfigurationManager::OpenFile(const char* config_file) 58 | { 59 | logger_->Write("ConfigurationManager", LogNotice, "OpenFile : %s", config_file); 60 | if (current_config_file_.compare( config_file) == 0) 61 | { 62 | // already openend 63 | return; 64 | } 65 | current_config_file_ = config_file; 66 | Clear(); 67 | std::string s, key, value; 68 | std::string current_section = ""; 69 | 70 | FIL File; 71 | FRESULT Result = f_open(&File, config_file, FA_READ | FA_OPEN_EXISTING); 72 | if (Result != FR_OK) 73 | { 74 | logger_->Write("ConfigurationManager", LogNotice, "Cannot open file: %s", config_file); 75 | return; 76 | } 77 | 78 | FILINFO file_info; 79 | f_stat(config_file, &file_info); 80 | unsigned char* buff = new unsigned char[file_info.fsize]; 81 | unsigned nBytesRead; 82 | 83 | f_read(&File, buff, file_info.fsize, &nBytesRead); 84 | if (file_info.fsize != nBytesRead) 85 | { 86 | // ERROR 87 | f_close(&File); 88 | logger_->Write("ConfigurationManager", LogNotice, "Read incorrect %i instead of ", nBytesRead, file_info.fsize); 89 | return; 90 | } 91 | 92 | logger_->Write("ConfigurationManager", LogNotice, "Read Config : %s ",buff); 93 | 94 | const char* ptr_buffer = (char*)buff; 95 | unsigned int offset = 0; 96 | unsigned int end_line; 97 | while ((end_line = getline(&ptr_buffer[offset], nBytesRead, s)) > 0) 98 | { 99 | offset += end_line; 100 | nBytesRead -= end_line; 101 | 102 | // Skip empty lines 103 | if (s.size() == 0) continue; 104 | 105 | std::string::size_type begin = s.find_first_not_of(" \f\t\v"); 106 | // Skip blank lines 107 | if (begin == std::string::npos) continue; 108 | 109 | // Skip commentary 110 | if ( s[begin] == '#' 111 | || s[begin] == ';' ) continue; 112 | 113 | 114 | std::string::size_type begin_section = s.find('[', begin); 115 | //if (begin_section != std::string::npos) 116 | if ( s[begin] == '[') 117 | { 118 | begin_section = begin; 119 | std::string::size_type end_section = s.find(']'); 120 | if (end_section != std::string::npos) 121 | { 122 | current_section = s.substr(begin_section+1, end_section - 1); 123 | s = s.substr(end_section+1); 124 | 125 | logger_->Write("ConfigurationManager", LogNotice, "READ section %s", current_section.c_str()); 126 | } 127 | } 128 | 129 | // Search key (if a section is already defined) 130 | if (current_section.size() > 0) 131 | { 132 | // Extract the key value 133 | std::string::size_type end = s.find('=', begin); 134 | 135 | if (end == std::string::npos) continue; 136 | 137 | logger_->Write("ConfigurationManager", LogNotice, "READ key/value %s", s.c_str()); 138 | key = s.substr(begin, end - begin); 139 | // (No leading or trailing whitespace allowed) 140 | size_t last_of_space = key.find_last_not_of(" \f\t\v") ; 141 | if ( last_of_space != std::string::npos) 142 | key.erase(last_of_space+ 1); 143 | 144 | // No blank keys allowed 145 | if (key.size() == 0) continue; 146 | 147 | // Extract the value (no leading or trailing whitespace allowed) 148 | begin = s.find_first_not_of(" \f\n\r\t\v", end + 1); 149 | if (begin == std::string::npos) 150 | { 151 | value = ""; 152 | } 153 | else 154 | { 155 | // Remove ending spaces 156 | end = s.find_last_not_of(" \f\n\r\t\v"); 157 | if ( end == std::string::npos) 158 | { 159 | logger_->Write("ConfigurationManager", LogNotice, "end == std::string::npos"); 160 | value = s.substr(begin); 161 | } 162 | else 163 | { 164 | logger_->Write("ConfigurationManager", LogNotice, "end != std::string::npos"); 165 | value = s.substr(begin, end-begin); 166 | } 167 | 168 | logger_->Write("ConfigurationManager", LogNotice, "READ key : %s", key.c_str()); 169 | logger_->Write("ConfigurationManager", LogNotice, "READ value: %s ", value.c_str()); 170 | 171 | // Add this key/value to current section 172 | Section* section; 173 | if (config_file_.GetSection (current_section.c_str(), section) == false) 174 | { 175 | Association new_section; 176 | new_section.key = current_section; 177 | section = new_section.value = new Section(); 178 | config_file_.push_back(new_section); 179 | } 180 | Association new_assoc; 181 | new_assoc.key = key; 182 | new_assoc.value = value; 183 | section->push_back(new_assoc); 184 | } 185 | } 186 | } 187 | delete []buff; 188 | f_close(&File); 189 | } 190 | 191 | void ConfigurationManager::CloseFile() 192 | { 193 | FIL File; 194 | FRESULT Result = f_open(&File, current_config_file_.c_str(), FA_WRITE | FA_CREATE_ALWAYS ); 195 | if (Result != FR_OK) 196 | { 197 | logger_->Write("ConfigurationManager", LogNotice, "Cannot open file: %s", current_config_file_.c_str()); 198 | return; 199 | } 200 | 201 | logger_->Write("ConfigurationManager", LogNotice, "File open"); 202 | 203 | // Write this file 204 | std::string output_file; 205 | for (auto const& ent1 : config_file_) 206 | { 207 | output_file.append("["); 208 | output_file.append(ent1.key); 209 | output_file.append("]\r\n"); 210 | logger_->Write("ConfigurationManager", LogNotice, "section %s", ent1.key.c_str()); 211 | for (auto const& ent2 : *ent1.value) 212 | { 213 | // ent2.first is the second key 214 | output_file.append (ent2.key); 215 | logger_->Write("ConfigurationManager", LogNotice, "key %s", ent2.key.c_str()); 216 | output_file.append ("="); 217 | output_file.append (ent2.value); 218 | logger_->Write("ConfigurationManager", LogNotice, "value %s", ent2.value.c_str()); 219 | output_file.append ("\r\n"); 220 | } 221 | } 222 | logger_->Write("ConfigurationManager", LogNotice, "Output file : %s", output_file.c_str()); 223 | unsigned nBytesRead; 224 | f_write (&File, output_file.c_str(), output_file.size(), &nBytesRead); 225 | f_close(&File); 226 | } 227 | 228 | void ConfigurationManager::SetConfiguration(const char* section, const char* key, const char* value, const char* file) 229 | { 230 | OpenFile(file); 231 | SetConfiguration(section, key, value); 232 | } 233 | 234 | void ConfigurationManager::SetConfiguration(const char* section_key, const char* key, const char* value) 235 | { 236 | logger_->Write("ConfigurationManager", LogNotice, "SetConfiguration : [%s] %s=%s", section_key, key ,value); 237 | 238 | Section* section; 239 | if (config_file_.GetSection (section_key, section) == false) 240 | { 241 | logger_->Write("ConfigurationManager", LogNotice, "Section not found"); 242 | Association new_section; 243 | new_section.key = section_key; 244 | section = new_section.value = new Section(); 245 | config_file_.push_back(new_section); 246 | } 247 | 248 | bool found = false; 249 | for (auto &it: *section) 250 | { 251 | if (strcmp ( it.key.c_str(), key) == 0) 252 | { 253 | it.value = value; 254 | found = true; 255 | } 256 | } 257 | 258 | if (!found) 259 | { 260 | logger_->Write("ConfigurationManager", LogNotice, "Key not found"); 261 | Association new_assoc; 262 | new_assoc.key = key; 263 | new_assoc.value = value; 264 | section->push_back(new_assoc); 265 | } 266 | 267 | char output_log_buffer[255]; 268 | GetConfiguration(section_key, key, "NOTHING", output_log_buffer, 255 ); 269 | 270 | for (auto const& ent1 : config_file_) 271 | { 272 | logger_->Write("ConfigurationManager", LogNotice, "SECTION : %s", ent1.key.c_str()); 273 | for (auto const& ent2 : *ent1.value) 274 | { 275 | logger_->Write("ConfigurationManager", LogNotice, "KEYS: %s = VALUE : %s", ent2.key.c_str(), ent2.value.c_str()); 276 | } 277 | } 278 | logger_->Write("ConfigurationManager", LogNotice, "EOF"); 279 | } 280 | 281 | unsigned int ConfigurationManager::GetConfiguration(const char* section, const char* key, const char* default_value, char* out_buffer, unsigned int buffer_size, const char* file) 282 | { 283 | OpenFile(file); 284 | return GetConfiguration(section, key, default_value, out_buffer, buffer_size); 285 | 286 | } 287 | 288 | unsigned int ConfigurationManager::GetConfiguration(const char* section_key, const char* key, const char* default_value, char* out_buffer, unsigned int buffer_size) 289 | { 290 | Section* section; 291 | if (config_file_.GetSection (section_key, section)) 292 | { 293 | std::string * value_str; 294 | if ( section->GetKey(key, value_str)) 295 | { 296 | strncpy ( out_buffer, value_str->c_str(), buffer_size); 297 | return strlen(out_buffer); 298 | } 299 | } 300 | strncpy(out_buffer, default_value, buffer_size); 301 | return 0; 302 | } 303 | 304 | unsigned int ConfigurationManager::GetConfigurationInt(const char* section_key, const char* key, unsigned int default_value, const char* file) 305 | { 306 | OpenFile(file); 307 | return GetConfigurationInt(section_key, key, default_value); 308 | } 309 | 310 | unsigned int ConfigurationManager::GetConfigurationInt(const char* section_key, const char* key, unsigned int default_value) 311 | { 312 | Section* section; 313 | if (config_file_.GetSection (section_key, section)) 314 | { 315 | std::string * value_str; 316 | if ( section->GetKey(key, value_str)) 317 | { 318 | return atoi(value_str->c_str()); 319 | } 320 | } 321 | return default_value; 322 | } 323 | 324 | const char* ConfigurationManager::GetFirstSection() 325 | { 326 | it_section_ = config_file_.begin(); 327 | return GetNextSection(); 328 | } 329 | 330 | const char* ConfigurationManager::GetNextSection() 331 | { 332 | if (it_section_ != config_file_.end()) 333 | { 334 | const char* value = it_section_->key.c_str(); 335 | ++it_section_; 336 | return value; 337 | } 338 | return nullptr; 339 | } 340 | 341 | const char* ConfigurationManager::GetFirstKey(const char* section_key) 342 | { 343 | Section* section; 344 | if (config_file_.GetSection (section_key, section)) 345 | { 346 | it_key_ = section->begin(); 347 | current_key_section_it_ = section; 348 | return GetNextKey(); 349 | } 350 | return nullptr; 351 | } 352 | 353 | const char* ConfigurationManager::GetNextKey() 354 | { 355 | if (it_key_ != current_key_section_it_->end()) 356 | { 357 | const char* value = it_key_->key.c_str(); 358 | ++it_key_; 359 | return value; 360 | } 361 | return nullptr; 362 | } 363 | 364 | bool ConfigurationManager::ConfigFile::GetSection (const char* section_name, ConfigurationManager::Section*& section) 365 | { 366 | // Look at section 367 | for (auto &it:*this) 368 | { 369 | if (strcmp ( it.key.c_str(), section_name) == 0) 370 | { 371 | section = it.value; 372 | return true; 373 | } 374 | } 375 | return false; 376 | } 377 | 378 | 379 | bool ConfigurationManager::Section::GetKey (const char* key_name, std::string*& key) 380 | { 381 | // Look at section 382 | for (auto it:*this) 383 | { 384 | if (strcmp ( it.key.c_str(), key_name) == 0) 385 | { 386 | key = &it.value; 387 | return true; 388 | } 389 | } 390 | return false; 391 | } 392 | 393 | 394 | -------------------------------------------------------------------------------- /src/ConfigurationManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "CPCCore/CPCCoreEmu/simple_string.h" 6 | #include "CPCCore/CPCCoreEmu/IConfiguration.h" 7 | #include "CPCCore/CPCCoreEmu/simple_vector.hpp" 8 | 9 | extern CLogger* log_s; 10 | 11 | class ConfigurationManager : public IConfiguration 12 | { 13 | public: 14 | 15 | ConfigurationManager(CLogger* log); 16 | virtual ~ConfigurationManager(); 17 | 18 | virtual void OpenFile(const char* config_file); 19 | virtual void CloseFile(); 20 | 21 | virtual void SetConfiguration(const char* section, const char* key, const char* value, const char* file); 22 | virtual void SetConfiguration(const char* section, const char* key, const char* value); 23 | 24 | virtual unsigned int GetConfiguration(const char* section, const char* cle, const char* default_value, char* out_buffer, unsigned int buffer_size, const char* file); 25 | virtual unsigned int GetConfiguration(const char* section, const char* cle, const char* default_value, char* out_buffer, unsigned int buffer_size); 26 | 27 | virtual unsigned int GetConfigurationInt(const char* section, const char* cle, unsigned int default_value, const char* file); 28 | virtual unsigned int GetConfigurationInt(const char* section, const char* cle, unsigned int default_value); 29 | 30 | // Section number 31 | virtual const char* GetFirstSection(); 32 | virtual const char* GetNextSection(); 33 | 34 | // Key 35 | virtual const char* GetFirstKey(const char* section); 36 | virtual const char* GetNextKey(); 37 | 38 | protected: 39 | void Clear(); 40 | 41 | template 42 | class Association 43 | { 44 | public: 45 | Association() : key(), value(){ 46 | //log_s->Write("Association", LogNotice, "default creator"); 47 | } 48 | Association(Association& assoc) 49 | { 50 | //log_s->Write("Association", LogNotice, "assoc creator"); 51 | key = assoc.key; 52 | value = assoc.value; 53 | } 54 | Association& operator=(const Association& _Right) 55 | { 56 | //log_s->Write("Association", LogNotice, "operator="); 57 | key = _Right.key; 58 | //log_s->Write("Association", LogNotice, "operator= 1"); 59 | value = _Right.value; 60 | //log_s->Write("Association", LogNotice, "operator done"); 61 | return *this; 62 | } 63 | 64 | std::string key; 65 | T value; 66 | }; 67 | 68 | class Section : public std::vector > 69 | { 70 | public: 71 | bool GetKey (const char* , std::string*&); 72 | }; 73 | 74 | class ConfigFile : public std::vector > 75 | { 76 | public: 77 | bool GetSection (const char* section, Section*&); 78 | }; 79 | 80 | //typedef std::map ConfigFile; 81 | 82 | ConfigFile config_file_; 83 | std::string current_config_file_; 84 | 85 | // Internal Iterator 86 | std::vector >::iterator it_section_; 87 | std::vector >::iterator it_key_; 88 | Section* current_key_section_it_; 89 | 90 | unsigned int getline ( const char*, int size, std::string& out); 91 | CLogger* logger_; 92 | }; 93 | -------------------------------------------------------------------------------- /src/DisplayPi.cpp: -------------------------------------------------------------------------------- 1 | // 2 | #include "DisplayPi.h" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "res/button_1.h" 11 | #include "res/coolspot.h" 12 | 13 | DisplayPi::DisplayPi(CLogger* logger, CTimer* timer) : 14 | logger_(logger), 15 | timer_(timer), 16 | frame_buffer_(nullptr), 17 | full_resolution_(false), 18 | full_resolution_cached_(false), 19 | mutex_(TASK_LEVEL), 20 | added_line_(1), 21 | buffer_used_(0), 22 | nb_frame_in_queue_(0), 23 | sync_on_frame_(false) 24 | { 25 | font_ = new CoolspotFont(logger_); 26 | 27 | for (int i = 0; i < FRAME_BUFFER_SIZE; i++) 28 | { 29 | frame_used_[i] = FR_FREE; 30 | } 31 | frame_used_[buffer_used_] = FR_USED; 32 | } 33 | 34 | DisplayPi::~DisplayPi() 35 | { 36 | delete font_; 37 | if ( frame_buffer_ != nullptr) 38 | { 39 | delete frame_buffer_; 40 | } 41 | } 42 | 43 | bool DisplayPi::Initialization() 44 | { 45 | /* 46 | unsigned int screen_width = 640; 47 | unsigned int screen_height = 480; 48 | 49 | // Get display property, to compute best values 50 | CBcmPropertyTags Tags; 51 | TPropertyTagDisplayDimensions Dimensions; 52 | if (Tags.GetTag (PROPTAG_GET_DISPLAY_DIMENSIONS, &Dimensions, sizeof Dimensions)) 53 | { 54 | screen_width = Dimensions.nWidth; 55 | screen_height = Dimensions.nHeight; 56 | } 57 | 58 | // Now we have real width/height : compute best values for display, to have : 59 | // - good pixel ratio (4x3) 60 | 61 | float ratio = ((float)GetWidth())/((float)GetHeight()); 62 | if ( (screen_width / screen_height ) < ratio ) 63 | { 64 | // 65 | NbPixelWidth = static_cast(screen_height * ratio); 66 | NbPixelHeight = screen_height ; 67 | } 68 | else 69 | { 70 | // 71 | NbPixelWidth = screen_width - 0; 72 | NbPixelHeight = static_cast(screen_width / ratio); 73 | } 74 | 75 | 76 | // Compute height to have a complete screen, without problem with scanlines 77 | if ( NbPixelHeight % m_Height != 0) 78 | { 79 | int NbPixelHeightComputed = (NbPixelHeight / m_Height) * m_Height; 80 | if ( NbPixelHeight % m_Height > (m_Height/2) ) 81 | { 82 | NbPixelHeightComputed += m_Height; 83 | } 84 | NbPixelHeight = NbPixelHeightComputed; 85 | NbPixelWidth = static_cast(NbPixelHeight*ratio); 86 | } 87 | 88 | int fXmin, fXmax, fYmin, fYmax; 89 | fXmin = (findMode.w - NbPixelWidth) / 2; 90 | fXmax = (findMode.w - NbPixelWidth) / 2 + NbPixelWidth; 91 | fYmin = ((long)findMode.h - NbPixelHeight) / 2; 92 | fYmax = ((long)findMode.h - NbPixelHeight) / 2 + NbPixelHeight; 93 | 94 | 95 | m_DestRectFullScreen.x = fXmin; 96 | m_DestRectFullScreen.y = fYmin; 97 | m_DestRectFullScreen.w = NbPixelWidth; 98 | m_DestRectFullScreen.h = NbPixelHeight;*/ 99 | // - 100 | 101 | 102 | if ( frame_buffer_ != nullptr) 103 | { 104 | delete frame_buffer_; 105 | } 106 | frame_buffer_ = new CBcmFrameBuffer(768, 277*2, 32, 1024, 1024* FRAME_BUFFER_SIZE); 107 | 108 | frame_buffer_->Initialize(); 109 | frame_buffer_->SetVirtualOffset(143, 47/2); 110 | 111 | return true; 112 | } 113 | 114 | bool DisplayPi::ListEDID() 115 | { 116 | // Display all resolution supported 117 | CBcmPropertyTags Tags; 118 | TPropertyTagEDIDBlock TagEDID; 119 | TagEDID.nBlockNumber = 0; 120 | bool tag_send = Tags.GetTag (PROPTAG_GET_EDID_BLOCK , &TagEDID, sizeof TagEDID, 4); 121 | if ( tag_send && TagEDID.nStatus == 0) 122 | { 123 | logger_->Write("Display", LogNotice, "EDID message : "); 124 | // Decodage : 125 | // check id 126 | unsigned char header[]={0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 }; 127 | if ( memcmp( &TagEDID.Block[0], header, sizeof(header)) == 0) 128 | { 129 | // Manufacturer ID, product, serial number 130 | // week/year 131 | // EDID version 132 | logger_->Write("Display", LogNotice, "EDID version : %i.%i", TagEDID.Block[18], TagEDID.Block[19]); 133 | Tags.GetTag (PROPTAG_GET_EDID_BLOCK , &TagEDID, sizeof TagEDID, 4); 134 | 135 | } 136 | else 137 | { 138 | logger_->Write("Display", LogNotice, "EDID Wrong header"); 139 | debug_hexdump (TagEDID.Block, 128, "EDID"); 140 | } 141 | } 142 | return true; 143 | } 144 | 145 | 146 | void DisplayPi::SetScanlines(int scan) 147 | { 148 | 149 | } 150 | 151 | bool DisplayPi::AFrameIsReady() 152 | { 153 | logger_->Write("Display", LogNotice, "A Frame is ready - NOT IMPLEMENTED "); 154 | return false; 155 | } 156 | 157 | void DisplayPi::Display() 158 | { 159 | logger_->Write("Display", LogNotice, "Display - NOT IMPLEMENTED "); 160 | } 161 | 162 | void DisplayPi::Config() 163 | { 164 | logger_->Write("Display", LogNotice, "Config - NOT IMPLEMENTED "); 165 | } 166 | 167 | const char* DisplayPi::GetInformations() 168 | { 169 | logger_->Write("Display", LogNotice, "Get Information "); 170 | return "Display for Raspberry PI - Bare metal"; 171 | } 172 | 173 | int DisplayPi::GetWidth() 174 | { 175 | return 1024; 176 | } 177 | 178 | int DisplayPi::GetHeight() 179 | { 180 | return 1024; 181 | } 182 | 183 | void DisplayPi::SetSize(IDisplay::SizeEnum size) 184 | { 185 | logger_->Write("Display", LogNotice, "SetSize - NOT IMPLEMENTED "); 186 | } 187 | 188 | IDisplay::SizeEnum DisplayPi::GetSize() 189 | { 190 | logger_->Write("Display", LogNotice, "SetSize - NOT IMPLEMENTED "); 191 | return IDisplay::S_STANDARD; 192 | } 193 | 194 | struct TBlankScreen 195 | { 196 | TPropertyTag Tag; 197 | u32 blank; 198 | 199 | } 200 | PACKED; 201 | 202 | void DisplayPi::Loop() 203 | { 204 | logger_->Write("DIS", LogNotice, "Starting loop"); 205 | // Waiting for a new frame to display 206 | //int old_frame_index = -1; 207 | while (1) 208 | { 209 | // Display available frame 210 | int frame_index = -1; 211 | mutex_.Acquire(); 212 | if (nb_frame_in_queue_ > 0) 213 | { 214 | /*if ( old_frame_index != -1) 215 | { 216 | frame_used_[old_frame_index] = FR_FREE; 217 | }*/ 218 | frame_index = frame_queue_[0]; 219 | nb_frame_in_queue_--; 220 | memmove (frame_queue_, &frame_queue_[1], nb_frame_in_queue_*sizeof(unsigned int)); 221 | 222 | //if (frame_index != -1) 223 | { 224 | //logger_->Write("DIS", LogNotice, "Loop : display %i - nb_frame_in_queue_ : %i", frame_index, nb_frame_in_queue_); 225 | mutex_.Release(); 226 | frame_buffer_->SetVirtualOffset(143, 47 / 2 + frame_index * 1024); 227 | frame_buffer_->WaitForVerticalSync(); 228 | // Set it as available 229 | frame_used_[frame_index] = FR_FREE; 230 | //old_frame_index = frame_index; 231 | } 232 | /*else 233 | { 234 | mutex_.Release(); 235 | logger_->Write("DIS", LogNotice, "No buffer to display"); 236 | CTimer::Get()->MsDelay(1); 237 | }*/ 238 | } 239 | else 240 | { 241 | mutex_.Release(); 242 | CTimer::Get()->MsDelay(1); 243 | } 244 | // sleep ? 245 | 246 | } 247 | } 248 | 249 | void DisplayPi::VSync(bool dbg ) 250 | { 251 | // Set current frame as ready 252 | //logger_->Write("DIS", LogNotice, "VSync : Frame ready is %i", buffer_used_); 253 | 254 | #ifndef USE_QEMU_SUGARPI 255 | bool clear_framebuffer = false; 256 | if (full_resolution_cached_ != full_resolution_) 257 | { 258 | clear_framebuffer = true; 259 | full_resolution_cached_ = full_resolution_; 260 | 261 | } 262 | 263 | if (true/*sync_on_frame_*/) // To turn on : Use the display core ! 264 | { 265 | frame_buffer_->SetVirtualOffset(143, 47 / 2 + buffer_used_ * 1024); 266 | if (sync_on_frame_) 267 | { 268 | frame_buffer_->WaitForVerticalSync(); 269 | } 270 | if (clear_framebuffer) 271 | { 272 | //unsigned char* line = (unsigned char*)(frame_buffer_->GetBuffer() + buffer_used_*1024*frame_buffer_->GetPitch()); 273 | unsigned char* line = reinterpret_cast(frame_buffer_->GetBuffer() + buffer_used_*1024*frame_buffer_->GetPitch()); 274 | for (unsigned int count = 0; count < 1024; count++) 275 | { 276 | memset(line, 0x0, 1024*4); 277 | line += frame_buffer_->GetPitch(); 278 | } 279 | } 280 | 281 | } 282 | else 283 | { 284 | mutex_.Acquire(); 285 | // get a new one (or wait for one to be free) 286 | bool found = false; 287 | 288 | for (int i = 0; i < FRAME_BUFFER_SIZE && !found; i++) 289 | { 290 | if (frame_used_[i] == FR_FREE) 291 | { 292 | frame_queue_[nb_frame_in_queue_++] = buffer_used_; 293 | frame_used_[buffer_used_] = FR_READY; 294 | //logger_->Write("DIS", LogNotice, "VSync : Add %i - nb_frame_in_queue_ : %i", buffer_used_, nb_frame_in_queue_); 295 | 296 | frame_used_[i] = FR_USED; 297 | buffer_used_ = i; 298 | found = true; 299 | break; 300 | } 301 | } 302 | if (!found) 303 | logger_->Write("DIS", LogNotice, "All buffers are used"); 304 | mutex_.Release(); 305 | } 306 | #else 307 | frame_buffer_->SetVirtualOffset(143, 47 / 2 * 1024); 308 | frame_buffer_->WaitForVerticalSync(); 309 | 310 | #endif 311 | //added_line_ ^= 1; 312 | //buffer_num_ ^= 1; 313 | added_line_ = 1; 314 | 315 | // static unsigned int count = 0; 316 | // static unsigned int max_tick = 0; 317 | // static unsigned int nb_long_frame = 0; 318 | 319 | // Frame is ready 320 | 321 | // wait for a new frame to be available 322 | 323 | 324 | 325 | 326 | // If last frame is more than 20ms, just don't do it 327 | 328 | /*frame_buffer_->WaitForVerticalSync(); 329 | unsigned int new_tick = timer_->GetClockTicks(); 330 | 331 | if (new_tick - last_tick_frame_ > max_tick) 332 | max_tick = new_tick - last_tick_frame_; 333 | 334 | if (new_tick - last_tick_frame_ > 20500) 335 | { 336 | nb_long_frame++; 337 | } 338 | 339 | if (++count == 500) 340 | { 341 | logger_->Write("DIS", LogNotice, "500frame : max_frame : %i; Nb frames > 20ms : %i", max_tick, nb_long_frame); 342 | max_tick = 0; 343 | count = 0; 344 | nb_long_frame = 0; 345 | } 346 | last_tick_frame_ = new_tick; 347 | */ 348 | 349 | #define PROPTAG_BLANK_SCREEN 0x00040002 350 | /*CBcmPropertyTags Tags; 351 | TBlankScreen blankScreen; 352 | blankScreen.blank = 0; 353 | if (Tags.GetTag(PROPTAG_BLANK_SCREEN, &blankScreen, sizeof blankScreen, 4)) 354 | { 355 | } 356 | else 357 | { 358 | logger_->Write("Display", LogNotice, "PROPTAG_BLANK_SCREEN - KO..."); 359 | } 360 | 361 | */ 362 | //logger_->Write("Display", LogNotice, "Vsync : added_line_=%i", added_line_); 363 | } 364 | 365 | // Start of sync 366 | void DisplayPi::StartSync() 367 | { 368 | logger_->Write("Display", LogNotice, "StartSync - NOT IMPLEMENTED "); 369 | } 370 | 371 | // Wait VBL 372 | void DisplayPi::WaitVbl() 373 | { 374 | frame_buffer_->WaitForVerticalSync(); 375 | } 376 | 377 | 378 | 379 | int* DisplayPi::GetVideoBuffer(int y) 380 | { 381 | if (!full_resolution_cached_) 382 | { 383 | y = y * 2 + added_line_; 384 | } 385 | 386 | 387 | y &= 0x3FF; 388 | y += buffer_used_ * 1024; 389 | 390 | ///// 391 | //logger_->Write("Display", LogNotice, "GetVideoBuffer : y = %i; buffer_used_ : %i, ==>%i", y, buffer_used_, frame_buffer_->GetBuffer() + y * frame_buffer_->GetPitch()); 392 | return reinterpret_cast(frame_buffer_->GetBuffer() + y * frame_buffer_->GetPitch()); 393 | 394 | // return (int*)(frame_buffer_->GetBuffer() + (y * 2 /*+ added_line_*/)* frame_buffer_->GetPitch() ); 395 | 396 | 397 | } 398 | 399 | void DisplayPi::Reset() 400 | { 401 | logger_->Write("Display", LogNotice, "Reset - NOT IMPLEMENTED "); 402 | } 403 | 404 | void DisplayPi::FullScreenToggle() 405 | { 406 | logger_->Write("Display", LogNotice, "FullScreenToggle - NOT IMPLEMENTED "); 407 | } 408 | 409 | void DisplayPi::ForceFullScreen(bool fullscreen) 410 | { 411 | logger_->Write("Display", LogNotice, "ForceFullScreen - NOT IMPLEMENTED "); 412 | } 413 | 414 | void DisplayPi::Screenshot() 415 | { 416 | logger_->Write("Display", LogNotice, "Screenshot - NOT IMPLEMENTED "); 417 | 418 | } 419 | 420 | void DisplayPi::ScreenshotEveryFrame(int on) 421 | { 422 | logger_->Write("Display", LogNotice, "ScreenshotEveryFrame - NOT IMPLEMENTED "); 423 | } 424 | 425 | bool DisplayPi::IsEveryFrameScreened() 426 | { 427 | logger_->Write("Display", LogNotice, "IsEveryFrameScreened - NOT IMPLEMENTED "); 428 | return false; 429 | } 430 | 431 | bool DisplayPi::SetSyncWithVbl(int speed) 432 | { 433 | logger_->Write("Display", LogNotice, "SetSyncWithVbl - NOT IMPLEMENTED "); 434 | return true; 435 | } 436 | 437 | bool DisplayPi::IsWaitHandled() 438 | { 439 | logger_->Write("Display", LogNotice, "IsWaitHandled - NOT IMPLEMENTED "); 440 | return true; 441 | } 442 | 443 | bool DisplayPi::IsDisplayed() 444 | { 445 | return true; 446 | } 447 | 448 | bool DisplayPi::GetBlackScreenInterval() 449 | { 450 | logger_->Write("Display", LogNotice, "GetBlackScreenInterval - NOT IMPLEMENTED "); 451 | return false; 452 | 453 | } 454 | 455 | void DisplayPi::SetBlackScreenInterval(bool on) 456 | { 457 | logger_->Write("Display", LogNotice, "SetBlackScreenInterval - NOT IMPLEMENTED "); 458 | } 459 | 460 | 461 | void DisplayPi::WindowChanged(int x_in, int y_in, int wnd_width, int wnd_height) 462 | { 463 | logger_->Write("Display", LogNotice, "WindowChanged - NOT IMPLEMENTED "); 464 | } 465 | 466 | void DisplayPi::ResetLoadingMedia() 467 | { 468 | logger_->Write("Display", LogNotice, "ResetLoadingMedia - NOT IMPLEMENTED "); 469 | } 470 | 471 | void DisplayPi::SetLoadingMedia() 472 | { 473 | logger_->Write("Display", LogNotice, "SetLoadingMedia - NOT IMPLEMENTED "); 474 | } 475 | 476 | void DisplayPi::ResetDragnDropDisplay() 477 | { 478 | logger_->Write("Display", LogNotice, "ResetDragnDropDisplay - NOT IMPLEMENTED "); 479 | 480 | } 481 | 482 | void DisplayPi::SetDragnDropDisplay(int type) 483 | { 484 | logger_->Write("Display", LogNotice, "SetDragnDropDisplay - NOT IMPLEMENTED "); 485 | } 486 | 487 | void DisplayPi::SetCurrentPart(int x, int y) 488 | { 489 | logger_->Write("Display", LogNotice, "SetCurrentPart - NOT IMPLEMENTED "); 490 | } 491 | 492 | int DisplayPi::GetDnDPart() 493 | { 494 | logger_->Write("Display", LogNotice, "GetDnDPart - NOT IMPLEMENTED "); 495 | return 0; 496 | } 497 | 498 | 499 | void DisplayPi::DisplayText(const char* txt, int x, int y, bool selected) 500 | { 501 | // Display text 502 | int i = 0; 503 | 504 | char buff[16]; 505 | memset(buff, 0, sizeof buff); 506 | strncpy(buff, txt, 15); 507 | 508 | unsigned int x_offset_output = 0; 509 | 510 | while (txt[i] != '\0' ) 511 | { 512 | 513 | // Display character 514 | unsigned char c = txt[i]; 515 | 516 | if ( c == ' ') 517 | { 518 | x_offset_output += 10; 519 | } 520 | else 521 | { 522 | // Look for proper bitmap position (on first line only) 523 | for (int display_y = 0; display_y < font_->GetLetterHeight(c); display_y++) 524 | { 525 | int* line = GetVideoBuffer(display_y + y); 526 | font_->CopyLetter(c, display_y, &line[x + x_offset_output]); 527 | } 528 | x_offset_output += font_->GetLetterLength(c); 529 | } 530 | i++; 531 | 532 | } 533 | } -------------------------------------------------------------------------------- /src/DisplayPi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | #include 5 | #include 6 | #include 7 | 8 | #include "CPCCore/CPCCoreEmu/Screen.h" 9 | 10 | #define FRAME_BUFFER_SIZE 2 11 | 12 | class CoolspotFont; 13 | 14 | class DisplayPi : public IDisplay 15 | { 16 | public: 17 | DisplayPi(CLogger* logger, CTimer* timer); 18 | virtual ~DisplayPi(); 19 | 20 | bool ListEDID(); 21 | 22 | bool Initialization(); 23 | void SyncWithFrame (bool set){sync_on_frame_ = set;} 24 | bool IsSyncOnFrame(){return sync_on_frame_;} 25 | 26 | void SetFullResolution (bool set){full_resolution_ = set;}; 27 | 28 | virtual void SetScanlines(int scan); 29 | virtual bool AFrameIsReady(); 30 | virtual void Display(); 31 | 32 | virtual void Config(); 33 | virtual const char* GetInformations(); 34 | virtual int GetWidth(); 35 | virtual int GetHeight(); 36 | virtual void SetSize(SizeEnum size); 37 | virtual SizeEnum GetSize(); 38 | virtual void VSync(bool dbg = false); 39 | 40 | // Start of sync 41 | virtual void StartSync(); 42 | // Wait VBL 43 | virtual void WaitVbl(); 44 | 45 | // Services 46 | void DisplayText(const char* txt, int x, int y, bool selected = false); 47 | 48 | virtual int* GetVideoBuffer(int y); 49 | virtual void Reset(); 50 | virtual void FullScreenToggle(); 51 | virtual void ForceFullScreen(bool fullscreen); 52 | virtual void Screenshot(); 53 | virtual void ScreenshotEveryFrame(int on); 54 | virtual bool IsEveryFrameScreened(); 55 | 56 | virtual bool SetSyncWithVbl(int speed); 57 | virtual bool IsWaitHandled(); 58 | virtual bool IsDisplayed(); 59 | virtual bool GetBlackScreenInterval(); 60 | virtual void SetBlackScreenInterval(bool on); 61 | 62 | virtual void WindowChanged(int x_in, int y_in, int wnd_width, int wnd_height); 63 | 64 | virtual void ResetLoadingMedia(); 65 | virtual void SetLoadingMedia(); 66 | 67 | virtual void ResetDragnDropDisplay(); 68 | virtual void SetDragnDropDisplay(int type); 69 | virtual void SetCurrentPart(int x, int y); 70 | virtual int GetDnDPart(); 71 | 72 | // Capability of device 73 | virtual bool CanVSync() { return true; } 74 | virtual bool CanInsertBlackFrame() { return false; } 75 | virtual void Activate(bool on) {}; 76 | 77 | CBcmFrameBuffer* GetFrameBuffer() { 78 | return frame_buffer_; } 79 | 80 | void Lock() { mutex_.Acquire(); } 81 | void Unlock() { mutex_.Release(); } 82 | 83 | void Loop(); 84 | 85 | protected: 86 | //CScreenDevice* screen_; 87 | CLogger* logger_; 88 | CTimer* timer_; 89 | CBcmFrameBuffer* frame_buffer_; 90 | bool full_resolution_; 91 | bool full_resolution_cached_; 92 | 93 | CSpinLock mutex_; 94 | 95 | unsigned int added_line_; 96 | unsigned int last_tick_frame_; 97 | 98 | // Frame buffer availability 99 | typedef enum 100 | { 101 | FR_FREE, 102 | FR_USED, 103 | FR_READY 104 | } FrameState; 105 | FrameState frame_used_[FRAME_BUFFER_SIZE]; 106 | unsigned int buffer_used_; 107 | 108 | unsigned int frame_queue_[FRAME_BUFFER_SIZE]; 109 | unsigned int nb_frame_in_queue_; 110 | 111 | bool sync_on_frame_; 112 | CoolspotFont *font_; 113 | }; -------------------------------------------------------------------------------- /src/KeyboardPi.cpp: -------------------------------------------------------------------------------- 1 | // 2 | #include "KeyboardPi.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define DEVICE_INDEX 1 // "upad1" 9 | 10 | // SET_KEYBOARD(0x59, 1, 5); // FN 1 11 | // SET_KEYBOARD(0x5A, 1, 6); // FN 2 12 | // SET_KEYBOARD(0x14, 8, 5); // A 13 | 14 | 15 | unsigned shift_l_modifier_ = 0x02; 16 | unsigned shift_r_modifier_ = 0x20; 17 | unsigned ctrl_modifier_ = 0x01; 18 | unsigned copy_modifier_ = 0x04; 19 | 20 | unsigned char default_raw_map[10][8] = 21 | { 22 | {0x52, 0x4F, 0x51, 0x61, 0x5E, 0x5B, 0x58, 0x63, }, // Cur_up Cur_right Cur_down F9 F6 F3 Enter F. 23 | {0x50, 0xE2, 0x5F, 0x60, 0x5D, 0x59, 0x5A, 0x62, }, // cur_left Copy f7 f8 f5 f1 f2 f0 24 | {0x4C, 0x30, 0x28, 0x32, 0x5C, 0xE5, 0x38, 0xE0, }, // Clr {[ Return }] F4 Shift `\ Ctrl 25 | {0x2E, 0x2D, 0x2F, 0x13, 0x34, 0x33, 0x2E, 0x37, }, // ^£ =- |@ P +; *: ?/ >, 26 | {0x27, 0x26, 0x12, 0x0C, 0x0F, 0x0E, 0x10, 0x36, }, // _0 )9 O I L K M <. 27 | {0x25, 0x24, 0x18, 0x1C, 0x0B, 0x0D, 0x11, 0x2C, }, // (8 '7 U Y H J N Space 28 | {0x23, 0x22, 0x15, 0x17, 0x0A, 0x09, 0x05, 0x19, }, // &,6,Joy1_Up %,5,Joy1_down, R,Joy1_Left T,Joy1_Right G,Joy1Fire2 F,Joy1Fire1 B V 29 | {0x21, 0x20, 0x08, 0x1A, 0x16, 0x07, 0x06, 0x1B, }, // $4 #3 E W S D C X 30 | {0x1E, 0x1F, 0x29, 0x14, 0x2B, 0x04, 0x39, 0x1D, }, // !1 "2 Esc Q Tab A CapsLock Z 31 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, } // Joy0up Joy0down Joy0left Joy0right Joy0F1 Joy0F2 unused Del 32 | }; 33 | 34 | GamepadActionHandler::GamepadActionHandler (unsigned char* line, unsigned int index, unsigned char* line2, unsigned int index2) : handler_(nullptr) 35 | { 36 | line_[0] = line; 37 | line_[1] = line2; 38 | 39 | index_[0] = index; 40 | index_[1] = index2; 41 | } 42 | 43 | GamepadActionHandler::~GamepadActionHandler() 44 | { 45 | // Delete handlers 46 | while (handler_ != nullptr) 47 | { 48 | Handler* next_handler = handler_->next_handler; 49 | delete handler_; 50 | handler_ = next_handler; 51 | } 52 | } 53 | 54 | void GamepadActionHandler::AddHandler(IGamepadPressed* handler) 55 | { 56 | Handler* next_handler = handler_; 57 | if ( handler_ == nullptr) 58 | { 59 | handler_ = new Handler; 60 | handler_->action_handler = handler; 61 | handler_->next_handler = nullptr; 62 | } 63 | else 64 | { 65 | while (next_handler->next_handler != nullptr) 66 | { 67 | next_handler = next_handler->next_handler; 68 | } 69 | next_handler->next_handler = new Handler; 70 | next_handler->next_handler->next_handler = nullptr; 71 | next_handler->next_handler->action_handler = handler; 72 | } 73 | } 74 | 75 | bool GamepadActionHandler::IsPressed(TGamePadState* state) 76 | { 77 | Handler* current_handler = handler_; 78 | while ( current_handler != nullptr) 79 | { 80 | if ( current_handler->action_handler->IsPressed(state)) 81 | return true; 82 | current_handler = current_handler->next_handler; 83 | } 84 | return false; 85 | } 86 | 87 | void GamepadActionHandler::UpdateMap(unsigned int nDeviceIndex, bool pressed) 88 | { 89 | if (pressed) 90 | { 91 | *line_[nDeviceIndex] &= ~(1<buttons & bit_to_test_; 109 | } 110 | protected: 111 | unsigned int bit_to_test_; 112 | }; 113 | 114 | class GamepadHatPressed : public IGamepadPressed 115 | { 116 | public: 117 | GamepadHatPressed (unsigned int hat_index, unsigned int value) : value_(value), hat_index_(hat_index) 118 | { 119 | } 120 | 121 | virtual bool IsPressed(TGamePadState* state) 122 | { 123 | return (state->hats[hat_index_] != 0xF) && (state->hats[hat_index_] & value_) == value_; 124 | } 125 | protected: 126 | unsigned int value_; 127 | unsigned int hat_index_; 128 | }; 129 | 130 | class GamepadAxisPressed : public IGamepadPressed 131 | { 132 | public: 133 | GamepadAxisPressed (unsigned int axis_index, bool axis_min) : axis_index_(axis_index), axis_min_(axis_min) 134 | { 135 | } 136 | 137 | virtual bool IsPressed(TGamePadState* state) 138 | { 139 | if ( axis_min_) 140 | { 141 | return state->axes[axis_index_].value == state->axes[axis_index_].minimum; 142 | } 143 | else 144 | { 145 | return state->axes[axis_index_].value == state->axes[axis_index_].maximum; 146 | } 147 | 148 | } 149 | protected: 150 | unsigned int axis_index_; 151 | bool axis_min_; 152 | }; 153 | 154 | ////////////////////////////////////// 155 | // Helper 156 | GamepadDef::GamepadDef(unsigned char* keymap) : supported_controls_(0), vid(0), pid(0), version(0), 157 | game_pad_button_X(&keymap[9], 4, &keymap[9], 4), 158 | game_pad_button_A(&keymap[9], 5, &keymap[9], 5), 159 | game_pad_button_up(&keymap[9], 0, &keymap[9], 0), 160 | game_pad_button_down(&keymap[9], 1, &keymap[9], 1), 161 | game_pad_button_left(&keymap[9], 2, &keymap[9], 2), 162 | game_pad_button_right(&keymap[9], 3, &keymap[9], 3), 163 | game_pad_button_start(&keymap[5], 7, &keymap[5], 7), 164 | game_pad_button_select(0, 0, 0, 0) 165 | { 166 | 167 | } 168 | 169 | IGamepadPressed* GamepadDef::CreateFunction(const char* value, bool min) 170 | { 171 | if (strlen (value) < 2) return nullptr; 172 | 173 | switch (value[0]) 174 | { 175 | case 'a': 176 | { 177 | unsigned int axis = atoi(&value [1]); 178 | return new GamepadAxisPressed(axis, min); 179 | break; 180 | } 181 | case 'b': 182 | { 183 | unsigned int button = atoi(&value [1]); 184 | return new GamepadButtonPressed(button); 185 | break; 186 | } 187 | case 'h': 188 | { 189 | // hat, value 190 | std::string str = &value[1]; 191 | size_t pos_middle = str.find ('.'); 192 | if ( pos_middle != std::string::npos) 193 | { 194 | std::string str_hat = str.substr(0, pos_middle); 195 | std::string str_value = str.substr(pos_middle+1); 196 | unsigned int hat = atoi(str_hat.c_str()); 197 | unsigned int value = atoi(str_value.c_str()); 198 | return new GamepadHatPressed( hat, value ); 199 | } 200 | } 201 | break; 202 | } 203 | return nullptr; 204 | } 205 | 206 | unsigned int GamepadDef::SetValue(const char* key, const char* value) 207 | { 208 | if ( strcmp(key, "a") == 0) 209 | { 210 | game_pad_button_A.AddHandler ( CreateFunction(value) ); 211 | supported_controls_ |= GamePadButtonA; 212 | } 213 | else if ( strcmp(key, "x") == 0) 214 | { 215 | game_pad_button_X.AddHandler ( CreateFunction(value) ); 216 | supported_controls_ |= GamePadButtonA; 217 | } 218 | else if ( strcmp(key, "dpdown") == 0) 219 | { 220 | game_pad_button_down.AddHandler ( CreateFunction(value) ); 221 | supported_controls_ |= GamePadButtonDown; 222 | } 223 | else if ( strcmp(key, "dpleft") == 0) 224 | { 225 | game_pad_button_left.AddHandler ( CreateFunction(value) ); 226 | supported_controls_ |= GamePadButtonLeft; 227 | } 228 | else if ( strcmp(key, "dpright") == 0) 229 | { 230 | game_pad_button_right.AddHandler ( CreateFunction(value) ); 231 | supported_controls_ |= GamePadButtonRight; 232 | } 233 | else if ( strcmp(key, "dpup") == 0) 234 | { 235 | game_pad_button_up.AddHandler ( CreateFunction(value) ); 236 | supported_controls_ |= GamePadButtonUp; 237 | } 238 | else if ( strcmp(key, "start") == 0) 239 | { 240 | game_pad_button_start.AddHandler ( CreateFunction(value) ); 241 | supported_controls_ |= GamePadButtonStart; 242 | } 243 | else if ( strcmp(key, "righttrigger") == 0) 244 | { 245 | game_pad_button_start.AddHandler ( CreateFunction(value) ); 246 | supported_controls_ |= GamePadButtonStart; 247 | } 248 | else if ( strcmp(key, "back") == 0) 249 | { 250 | game_pad_button_select.AddHandler ( CreateFunction(value) ); 251 | supported_controls_ |= GamePadButtonSelect; 252 | } 253 | if ( strcmp(key, "lefttrigger") == 0) 254 | { 255 | game_pad_button_select.AddHandler ( CreateFunction(value) ); 256 | supported_controls_ |= GamePadButtonSelect; 257 | } 258 | else if ( strcmp(key, "leftx") == 0) 259 | { 260 | game_pad_button_left.AddHandler ( CreateFunction(value, true) ); 261 | game_pad_button_right.AddHandler ( CreateFunction(value, false) ); 262 | supported_controls_ |= GamePadButtonLeft; 263 | supported_controls_ |= GamePadButtonRight; 264 | } 265 | else if ( strcmp(key, "lefty") == 0) 266 | { 267 | game_pad_button_down.AddHandler ( CreateFunction(value, false) ); 268 | game_pad_button_up.AddHandler ( CreateFunction(value, true) ); 269 | supported_controls_ |= GamePadButtonUp; 270 | supported_controls_ |= GamePadButtonDown; 271 | } 272 | return true; 273 | } 274 | 275 | 276 | typedef char t_id[9]; 277 | 278 | KeyboardPi* KeyboardPi::this_ptr_ = 0; 279 | 280 | 281 | unsigned int getline ( const char* buffer, int size, std::string& out) 282 | { 283 | if ( size == 0) 284 | { 285 | return 0; 286 | } 287 | 288 | // looking for /n 289 | int offset = 0; 290 | while (buffer[offset] != 0x0A && buffer[offset] != 0x0D && offset < size) 291 | { 292 | offset++; 293 | } 294 | 295 | char* line = new char[offset+1]; 296 | memcpy ( line, buffer, offset); 297 | line[offset] = '\0'; 298 | out = std::string(line); 299 | delete []line; 300 | return (offset == size)?offset:offset+1; 301 | } 302 | 303 | KeyboardPi::KeyboardPi(CLogger* logger, CUSBHCIDevice* dwhci_device, CDeviceNameService* device_name_service) : 304 | logger_(logger), 305 | device_name_service_(device_name_service), 306 | dwhci_device_(dwhci_device), 307 | action_buttons_(0), 308 | select_(false) 309 | { 310 | memset ( keyboard_lines_, 0xff, sizeof (keyboard_lines_)); 311 | 312 | InitKeyboard (default_raw_map); 313 | this_ptr_ = this; 314 | 315 | for (unsigned i = 0; i < MAX_GAMEPADS; i++) 316 | { 317 | gamepad_[i] = 0; 318 | gamepad_active_ [i] = nullptr; 319 | } 320 | 321 | memset(&gamepad_state_buffered_, 0, sizeof(gamepad_state_buffered_)); 322 | memset(&gamepad_state_, 0, sizeof(gamepad_state_)); 323 | } 324 | 325 | KeyboardPi::~KeyboardPi() 326 | { 327 | 328 | } 329 | 330 | #define SET_KEYBOARD(raw,line,b)\ 331 | raw_to_cpc_map_[raw].line_index = &keyboard_lines_[line];\ 332 | raw_to_cpc_map_[raw].bit = 1<Initialize() == false) 358 | { 359 | logger_->Write("Keyboard", LogPanic, "Initialize failed !"); 360 | } 361 | logger_->Write("Keyboard", LogNotice, "Initialize done."); 362 | 363 | UpdatePlugnPlay(); 364 | 365 | return true; 366 | } 367 | void KeyboardPi::UpdatePlugnPlay() 368 | { 369 | boolean bUpdated = dwhci_device_->UpdatePlugAndPlay (); 370 | 371 | if (bUpdated) 372 | { 373 | // Gamepad 374 | for (unsigned nDevice = 1; (nDevice <= MAX_GAMEPADS); nDevice++) 375 | { 376 | if (gamepad_[nDevice-1] != 0) 377 | { 378 | continue; 379 | } 380 | 381 | gamepad_[nDevice-1] = (CUSBGamePadDevice *)device_name_service_->GetDevice ("upad", nDevice, FALSE); 382 | if (gamepad_[nDevice-1] == 0) 383 | { 384 | continue; 385 | } 386 | 387 | // Get gamepad names 388 | CString* gamepad_name = gamepad_[nDevice-1]->GetDevice()->GetNames(); 389 | const TUSBDeviceDescriptor* descriptor = gamepad_[nDevice-1]->GetDevice()->GetDeviceDescriptor(); 390 | 391 | logger_->Write ("Keyboard", LogNotice, "Gamepad : %s - VID=%X; PID=%X; bcdDevice = %X", (const char*) (*gamepad_name), descriptor->idVendor, 392 | descriptor->idProduct, descriptor->bcdDevice ); 393 | delete gamepad_name ; 394 | const TGamePadState *pState = gamepad_[nDevice-1]->GetInitialState (); 395 | assert (pState != 0); 396 | 397 | memcpy(&gamepad_state_[nDevice-1], pState, sizeof (TGamePadState)); 398 | logger_->Write ("Keyboard", LogNotice, "Gamepad %u: %d Button(s) %d Hat(s)", 399 | nDevice, pState->nbuttons, pState->nhats); 400 | 401 | gamepad_[nDevice-1]->RegisterRemovedHandler (GamePadRemovedHandler, this); 402 | gamepad_[nDevice-1]->RegisterStatusHandler (GamePadStatusHandler); 403 | gamepad_active_[nDevice-1] = LookForDevice (descriptor); 404 | 405 | logger_->Write ("Keyboard", LogNotice, "Use your gamepad controls!"); 406 | } 407 | 408 | // Keyboard 409 | if (keyboard_ == 0) 410 | { 411 | keyboard_ = (CUSBKeyboardDevice *) device_name_service_->GetDevice ("ukbd1", FALSE); 412 | if (keyboard_ != 0) 413 | { 414 | keyboard_->RegisterRemovedHandler (KeyboardRemovedHandler); 415 | keyboard_->RegisterKeyStatusHandlerRaw (KeyStatusHandlerRaw); 416 | 417 | logger_->Write ("Keyboard", LogNotice, "Just type something!"); 418 | } 419 | } 420 | } 421 | } 422 | 423 | unsigned char KeyboardPi::GetKeyboardMap(int index) 424 | { 425 | unsigned char result = 0xFF; 426 | mutex_.Acquire(); 427 | 428 | result = keyboard_lines_[index]; 429 | 430 | mutex_.Release(); 431 | return result; 432 | } 433 | 434 | bool KeyboardPi::AddAction (GamepadActionHandler* action, unsigned nDeviceIndex, bool update_map) 435 | { 436 | if ( action == nullptr ) return false; 437 | bool x = action->IsPressed(&gamepad_state_[nDeviceIndex]); 438 | 439 | if (update_map) 440 | { 441 | action->UpdateMap(nDeviceIndex, x); 442 | } 443 | 444 | bool buff_x = action->IsPressed(&gamepad_state_buffered_[nDeviceIndex]); 445 | return ( (buff_x & x)^x); 446 | } 447 | 448 | void KeyboardPi::CheckActions (unsigned nDeviceIndex) 449 | { 450 | if ( gamepad_active_[nDeviceIndex] == nullptr) return; 451 | mutex_.Acquire(); 452 | action_buttons_ |= AddAction(&gamepad_active_[nDeviceIndex]->game_pad_button_X, nDeviceIndex, true)?GamePadButtonX:0; 453 | action_buttons_ |= AddAction(&gamepad_active_[nDeviceIndex]->game_pad_button_A, nDeviceIndex, true)?GamePadButtonA:0; 454 | action_buttons_ |= AddAction(&gamepad_active_[nDeviceIndex]->game_pad_button_up, nDeviceIndex, true)?GamePadButtonUp:0; 455 | action_buttons_ |= AddAction(&gamepad_active_[nDeviceIndex]->game_pad_button_down, nDeviceIndex, true)?GamePadButtonDown:0; 456 | action_buttons_ |= AddAction(&gamepad_active_[nDeviceIndex]->game_pad_button_left, nDeviceIndex, true)?GamePadButtonLeft:0; 457 | action_buttons_ |= AddAction(&gamepad_active_[nDeviceIndex]->game_pad_button_right, nDeviceIndex, true)?GamePadButtonRight:0; 458 | action_buttons_ |= AddAction(&gamepad_active_[nDeviceIndex]->game_pad_button_start, nDeviceIndex, true)?GamePadButtonStart:0; 459 | action_buttons_ |= AddAction(&gamepad_active_[nDeviceIndex]->game_pad_button_select, nDeviceIndex, true)?GamePadButtonSelect:0; 460 | mutex_.Release(); 461 | } 462 | 463 | void KeyboardPi::Init(bool* register_replaced) 464 | { 465 | 466 | } 467 | 468 | void KeyboardPi::ClearBuffer() 469 | { 470 | action_buttons_ = 0; 471 | select_ = false; 472 | } 473 | 474 | bool KeyboardPi::IsSelect() 475 | { 476 | return select_; 477 | } 478 | 479 | bool KeyboardPi::IsDown() 480 | { 481 | if (action_buttons_ & (GamePadButtonDown)) 482 | { 483 | mutex_.Acquire(); 484 | action_buttons_ &= ~(GamePadButtonDown); 485 | mutex_.Release(); 486 | return true; 487 | } 488 | else 489 | { 490 | return false; 491 | } 492 | 493 | } 494 | 495 | bool KeyboardPi::IsUp() 496 | { 497 | if (action_buttons_ & (GamePadButtonUp)) 498 | { 499 | mutex_.Acquire(); 500 | action_buttons_ &= ~(GamePadButtonUp); 501 | mutex_.Release(); 502 | return true; 503 | } 504 | else 505 | { 506 | return false; 507 | } 508 | } 509 | 510 | bool KeyboardPi::IsAction() 511 | { 512 | if (action_buttons_ & (GamePadButtonA|GamePadButtonX)) 513 | { 514 | mutex_.Acquire(); 515 | action_buttons_ &= ~(GamePadButtonA | GamePadButtonX); 516 | mutex_.Release(); 517 | return true; 518 | } 519 | else 520 | { 521 | return false; 522 | } 523 | } 524 | 525 | 526 | void KeyboardPi::ReinitSelect() 527 | { 528 | select_ = false; 529 | } 530 | 531 | void KeyboardPi::KeyStatusHandlerRaw (unsigned char ucModifiers, const unsigned char RawKeys[6]) 532 | { 533 | assert (this_ptr_ != 0); 534 | 535 | CString Message; 536 | Message.Format ("Key status (modifiers %02X)", (unsigned) ucModifiers); 537 | 538 | this_ptr_->mutex_.Acquire(); 539 | 540 | // Modifier 541 | if (this_ptr_->old_modifier_ & shift_l_modifier_) this_ptr_->keyboard_lines_[2] |= 0x20; 542 | if (this_ptr_->old_modifier_ & shift_r_modifier_) this_ptr_->keyboard_lines_[2] |= 0x20; 543 | if (this_ptr_->old_modifier_ & ctrl_modifier_) this_ptr_->keyboard_lines_[2] |= 0x80; 544 | if (this_ptr_->old_modifier_ & copy_modifier_) this_ptr_->keyboard_lines_[1] |= 0x02; 545 | 546 | // Unpress the previous keys 547 | for (unsigned i = 0; i < 6; i++) 548 | { 549 | if (this_ptr_->old_raw_keys_[i] != 0) 550 | { 551 | if (this_ptr_->raw_to_cpc_map_[this_ptr_->old_raw_keys_[i]].bit != 0) 552 | { 553 | *this_ptr_->raw_to_cpc_map_[this_ptr_->old_raw_keys_[i]].line_index |= (this_ptr_->raw_to_cpc_map_[this_ptr_->old_raw_keys_[i]].bit); 554 | } 555 | } 556 | } 557 | 558 | // Press the new ones 559 | if (ucModifiers & shift_l_modifier_) this_ptr_->keyboard_lines_[2] &= ~0x20; 560 | if (ucModifiers & shift_r_modifier_) this_ptr_->keyboard_lines_[2] &= ~0x20; 561 | if (ucModifiers & ctrl_modifier_) this_ptr_->keyboard_lines_[2] &= ~0x80; 562 | if (ucModifiers & copy_modifier_) this_ptr_->keyboard_lines_[1] &= ~0x02; 563 | 564 | for (unsigned i = 0; i < 6; i++) 565 | { 566 | if (RawKeys[i] != 0) 567 | { 568 | if (this_ptr_->raw_to_cpc_map_[RawKeys[i]].bit != 0) 569 | { 570 | *this_ptr_->raw_to_cpc_map_[RawKeys[i]].line_index &= ~(this_ptr_->raw_to_cpc_map_[RawKeys[i]].bit); 571 | } 572 | 573 | CString KeyCode; 574 | KeyCode.Format (" %02X", (unsigned) RawKeys[i]); 575 | 576 | Message.Append (KeyCode); 577 | } 578 | } 579 | this_ptr_->old_modifier_ = ucModifiers; 580 | memcpy( this_ptr_->old_raw_keys_, RawKeys, sizeof (this_ptr_->old_raw_keys_)); 581 | this_ptr_->mutex_.Release(); 582 | 583 | //CLogger::Get ()->Write ("Keyboard", LogNotice, Message); 584 | } 585 | 586 | void KeyboardPi::KeyboardRemovedHandler (CDevice *pDevice, void *pContext) 587 | { 588 | KeyboardPi *pThis = (KeyboardPi *) pContext; 589 | assert (pThis != 0); 590 | 591 | CLogger::Get ()->Write ("Keyboard", LogDebug, "Keyboard removed"); 592 | 593 | pThis->keyboard_ = 0; 594 | } 595 | 596 | void KeyboardPi::GamePadRemovedHandler (CDevice *pDevice, void *pContext) 597 | { 598 | KeyboardPi *pThis = (KeyboardPi *) pContext; 599 | assert (pThis != 0); 600 | 601 | for (unsigned i = 0; i < MAX_GAMEPADS; i++) 602 | { 603 | if (pThis->gamepad_[i] == (CUSBGamePadDevice *) pDevice) 604 | { 605 | CLogger::Get ()->Write ("Keyboard", LogDebug, "Gamepad %u removed", i+1); 606 | 607 | pThis->gamepad_[i] = 0; 608 | 609 | break; 610 | } 611 | } 612 | } 613 | 614 | void KeyboardPi::GamePadStatusHandler(unsigned nDeviceIndex, const TGamePadState* pState) 615 | { 616 | assert(this_ptr_ != 0); 617 | assert(pState != 0); 618 | 619 | memcpy(&this_ptr_->gamepad_state_[nDeviceIndex], pState, sizeof * pState); 620 | // Set the new pushed buttons 621 | 622 | this_ptr_->CheckActions (nDeviceIndex); 623 | if (( this_ptr_->gamepad_active_[nDeviceIndex] != nullptr) && this_ptr_->AddAction(&this_ptr_->gamepad_active_[nDeviceIndex]->game_pad_button_select, nDeviceIndex)) 624 | { 625 | this_ptr_->select_ = true; 626 | } 627 | 628 | this_ptr_->gamepad_state_buffered_[nDeviceIndex] = this_ptr_->gamepad_state_[nDeviceIndex]; 629 | } 630 | 631 | 632 | #define GAMECONTROLLERDB_FILE "SD:/Config/gamecontrollerdb.txt" 633 | GamepadDef* KeyboardPi::LookForDevice (const TUSBDeviceDescriptor* descriptor) 634 | { 635 | GamepadDef* gamepad = nullptr; 636 | 637 | for (unsigned int index = 0; index < gamepad_list_.size(); index++) 638 | { 639 | if ( gamepad_list_[index]->vid == descriptor->idVendor && gamepad_list_[index]->pid == descriptor->idProduct && gamepad_list_[index]->version == descriptor->bcdDevice) 640 | { 641 | logger_->Write("KeyboardPi", LogNotice, "Gamepad found in database !"); 642 | return gamepad_list_[index]; 643 | } 644 | } 645 | 646 | logger_->Write("KeyboardPi", LogNotice, "Unknown gamepad..."); 647 | return gamepad; 648 | } 649 | 650 | void KeyboardPi::LoadGameControllerDB() 651 | { 652 | logger_->Write("KeyboardPi", LogNotice, "Loading game controller db..."); 653 | 654 | // Open file 655 | FIL File; 656 | FRESULT Result = f_open(&File, GAMECONTROLLERDB_FILE, FA_READ | FA_OPEN_EXISTING); 657 | if (Result != FR_OK) 658 | { 659 | CLogger::Get ()->Write("ConfigurationManager", LogNotice, "Cannot open %s file", GAMECONTROLLERDB_FILE); 660 | return; 661 | } 662 | 663 | // Load every known gamepad to internal structure 664 | FILINFO file_info; 665 | f_stat(GAMECONTROLLERDB_FILE, &file_info); 666 | unsigned char* buff = new unsigned char[file_info.fsize]; 667 | unsigned nBytesRead; 668 | 669 | f_read(&File, buff, file_info.fsize, &nBytesRead); 670 | if (file_info.fsize != nBytesRead) 671 | { 672 | // ERROR 673 | f_close(&File); 674 | logger_->Write("KeyboardPi", LogNotice, "Error reading gamecontrollerdb "); 675 | return; 676 | } 677 | 678 | // get next line 679 | gamepad_list_.clear(); 680 | const char* ptr_buffer = (char*)buff; 681 | unsigned int offset = 0; 682 | unsigned int end_line; 683 | std::string s; 684 | while ((end_line = getline(&ptr_buffer[offset], nBytesRead, s)) > 0) 685 | { 686 | offset += end_line; 687 | nBytesRead -= end_line; 688 | 689 | // Do not use emty lines, and comment lines 690 | if (s.size() == 0 ||s[0] == '#') 691 | { 692 | continue; 693 | } 694 | 695 | // read : vid/pid 696 | t_id num_buffer[4]; 697 | memset (num_buffer, 0, sizeof(num_buffer)); 698 | bool error = false; 699 | for (int i = 0; i < 4 && !error; i++) 700 | { 701 | std::string id = s.substr(i*8, 4); 702 | strcpy(num_buffer[i], id.substr(2, 2).c_str() ); 703 | strcat(num_buffer[i], id.substr(0, 2).c_str() ); 704 | } 705 | 706 | if (error) continue; 707 | 708 | GamepadDef * def = new GamepadDef(keyboard_lines_); 709 | 710 | char* ptr; 711 | 712 | def->vid = strtoul(num_buffer[1], &ptr, 16); 713 | def->pid = strtoul(num_buffer[2], &ptr, 16); 714 | def->version = strtoul(num_buffer[3], &ptr, 16); 715 | 716 | // 717 | 718 | // remove ids 719 | s = s.substr(33); 720 | // extract name (until next comma) 721 | std::string::size_type end_name = s.find (','); 722 | if (end_name == std::string::npos) continue; 723 | 724 | def->name = s.substr (0, end_name); 725 | s = s.substr (end_name+1); 726 | 727 | 728 | // Do not handle native circle++ handled device: 729 | if ( ( def->vid == 0x54C && def->pid == 0x268) 730 | ||( def->vid == 0x54C && def->pid == 0x9cc) 731 | ||( def->vid == 0x54C && def->pid == 0x5c4) 732 | ||( def->vid == 0x45e && def->pid == 0x28e) 733 | ||( def->vid == 0x45e && def->pid == 0x28f) 734 | ||( def->vid == 0x45e && def->pid == 0x2d1) 735 | ||( def->vid == 0x45e && def->pid == 0x2dd) 736 | ||( def->vid == 0x45e && def->pid == 0x2e3) 737 | ||( def->vid == 0x45e && def->pid == 0x2ea) 738 | ||( def->vid == 0x57e && def->pid == 0x2009) 739 | ) 740 | { 741 | def->game_pad_button_X.AddHandler(new GamepadButtonPressed(10/*GamePadButtonX*/)); 742 | def->game_pad_button_A.AddHandler(new GamepadButtonPressed(9/*GamePadButtonA*/)); 743 | def->game_pad_button_up.AddHandler(new GamepadButtonPressed(15/*GamePadButtonUp*/)); 744 | def->game_pad_button_down.AddHandler(new GamepadButtonPressed(17/*GamePadButtonDown*/)); 745 | def->game_pad_button_left.AddHandler(new GamepadButtonPressed(18/*GamePadButtonLeft*/)); 746 | def->game_pad_button_right.AddHandler(new GamepadButtonPressed(16/*GamePadButtonRight*/)); 747 | def->game_pad_button_select.AddHandler(new GamepadButtonPressed(11/*GamePadButtonSelect*/)); 748 | def->game_pad_button_start.AddHandler(new GamepadButtonPressed(14/*GamePadButtonStart*/)); 749 | } 750 | else 751 | { 752 | // extract buttons, axis, etc. Everything has the form : x:y, 753 | std::string::size_type end_str = s.find (','); 754 | while (end_str != std::string::npos) 755 | { 756 | std::string parameter = s.substr (0, end_str); 757 | size_t pos_middle = parameter.find (':'); 758 | if ( pos_middle != std::string::npos) 759 | { 760 | std::string key = parameter.substr(0, pos_middle); 761 | std::string value = parameter.substr(pos_middle+1); 762 | 763 | // Affect to proper attribute. 764 | def->SetValue(key.c_str(), value.c_str()); 765 | } 766 | 767 | s = s.substr (end_str+1); 768 | end_str = s.find (','); 769 | } 770 | } 771 | 772 | // Add to controller database 773 | gamepad_list_.push_back (def); 774 | } 775 | 776 | delete []buff; 777 | f_close(&File); 778 | 779 | logger_->Write("KeyboardPi", LogNotice, "Loading game controller db... Done !"); 780 | } 781 | 782 | void KeyboardPi::LoadKeyboardLayout (const char* path) 783 | { 784 | // Open file 785 | FIL File; 786 | FRESULT Result = f_open(&File, path, FA_READ | FA_OPEN_EXISTING); 787 | if (Result != FR_OK) 788 | { 789 | CLogger::Get ()->Write("ConfigurationManager", LogNotice, "Cannot open %s layout file", path); 790 | return; 791 | } 792 | 793 | // Load every known gamepad to internal structure 794 | FILINFO file_info; 795 | f_stat(path, &file_info); 796 | unsigned char* buff = new unsigned char[file_info.fsize]; 797 | unsigned nBytesRead; 798 | 799 | f_read(&File, buff, file_info.fsize, &nBytesRead); 800 | if (file_info.fsize != nBytesRead) 801 | { 802 | // ERROR 803 | f_close(&File); 804 | logger_->Write("KeyboardPi", LogNotice, "Error reading keyboard layout file %s ",path ); 805 | return; 806 | } 807 | 808 | // get next line 809 | const char* ptr_buffer = (char*)buff; 810 | unsigned int offset = 0; 811 | unsigned int end_line; 812 | std::string s; 813 | int line_index = 0; 814 | while ((end_line = getline(&ptr_buffer[offset], nBytesRead, s)) > 0 && line_index < 8) 815 | { 816 | nBytesRead -= end_line; 817 | 818 | // Do not use emty lines, and comment lines 819 | if (s.size() == 0 ||s[0] == '#') 820 | { 821 | continue; 822 | } 823 | 824 | // Decode line to buffer 825 | for (unsigned int raw_key = 0; raw_key<8 && (2+raw_key * 3) < end_line; raw_key++) 826 | { 827 | char number [3]; 828 | memcpy ( number, &ptr_buffer[offset+raw_key*3], 2); 829 | number[2] = '\0'; 830 | unsigned char value = strtoul(number, NULL, 16); 831 | default_raw_map[line_index][raw_key] = value; 832 | } 833 | offset += end_line; 834 | line_index++; 835 | } 836 | delete []buff; 837 | f_close(&File); 838 | 839 | InitKeyboard (default_raw_map); 840 | 841 | logger_->Write("KeyboardPi", LogNotice, "Loading keyboard layout... Done !"); 842 | } 843 | -------------------------------------------------------------------------------- /src/KeyboardPi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "CPCCore/CPCCoreEmu/IKeyboard.h" 10 | #include "CPCCore/CPCCoreEmu/simple_string.h" 11 | #include "CPCCore/CPCCoreEmu/simple_vector.hpp" 12 | 13 | #define MAX_GAMEPADS 2 14 | 15 | 16 | class IGamepadPressed 17 | { 18 | public: 19 | virtual bool IsPressed(TGamePadState*) = 0; 20 | }; 21 | 22 | class GamepadActionHandler 23 | { 24 | public: 25 | GamepadActionHandler (unsigned char* line, unsigned int index, unsigned char* line2, unsigned int index2); 26 | virtual ~GamepadActionHandler(); 27 | 28 | void AddHandler(IGamepadPressed*); 29 | bool IsPressed(TGamePadState*); 30 | void UpdateMap(unsigned int nDeviceIndex, bool pressed); 31 | 32 | protected: 33 | struct Handler 34 | { 35 | IGamepadPressed* action_handler; 36 | Handler* next_handler; 37 | }; 38 | Handler* handler_; 39 | unsigned char* line_[2]; 40 | unsigned int index_[2]; 41 | }; 42 | 43 | class GamepadDef 44 | { 45 | public: 46 | GamepadDef(unsigned char* keymap); 47 | virtual ~GamepadDef() 48 | { 49 | } 50 | 51 | unsigned int SetValue(const char* key, const char* value); 52 | IGamepadPressed* CreateFunction(const char* value, bool min = true); 53 | 54 | // Attributes 55 | std::string name; 56 | 57 | unsigned int supported_controls_; 58 | unsigned int vid; 59 | unsigned int pid; 60 | unsigned int version; 61 | 62 | // Values for buttons 63 | bool IsPressed ( TGamePadState* ); 64 | 65 | GamepadActionHandler game_pad_button_X; 66 | GamepadActionHandler game_pad_button_A; 67 | GamepadActionHandler game_pad_button_up; 68 | GamepadActionHandler game_pad_button_down; 69 | GamepadActionHandler game_pad_button_left; 70 | GamepadActionHandler game_pad_button_right; 71 | GamepadActionHandler game_pad_button_start; 72 | GamepadActionHandler game_pad_button_select; 73 | 74 | }; 75 | 76 | class KeyboardPi : public IKeyboardHandler 77 | { 78 | public: 79 | KeyboardPi(CLogger* logger, CUSBHCIDevice* dwhci_device, CDeviceNameService* device_name_service); 80 | virtual ~KeyboardPi(); 81 | 82 | bool Initialize(); 83 | void InitKeyboard (unsigned char key_map[10][8]); 84 | void LoadKeyboardLayout (const char* path); 85 | 86 | virtual unsigned char GetKeyboardMap(int index); 87 | void UpdatePlugnPlay(); 88 | void Init(bool* register_replaced); 89 | void ForceKeyboardState(unsigned char key_states[10]) {}; 90 | 91 | bool AddAction (GamepadActionHandler* action, unsigned nDeviceIndex, bool update_map = false); 92 | void CheckActions(unsigned nDeviceIndex) ; 93 | 94 | void ClearBuffer(); 95 | bool IsSelect(); 96 | bool IsDown(); 97 | bool IsUp(); 98 | bool IsAction(); 99 | void ReinitSelect(); 100 | 101 | static void GamePadRemovedHandler (CDevice *pDevice, void *pContext); 102 | static void GamePadStatusHandler(unsigned nDeviceIndex, const TGamePadState* pState); 103 | static void KeyStatusHandlerRaw (unsigned char ucModifiers, const unsigned char RawKeys[6]); 104 | static void KeyboardRemovedHandler (CDevice *pDevice, void *pContext); 105 | protected: 106 | void LoadGameControllerDB(); 107 | GamepadDef* LookForDevice (const TUSBDeviceDescriptor* descriptor); 108 | //void UpdateKeyboardMap(); 109 | 110 | CLogger* logger_; 111 | CDeviceNameService* device_name_service_; 112 | CUSBHCIDevice *dwhci_device_; 113 | CUSBGamePadDevice* gamepad_[MAX_GAMEPADS]; 114 | CUSBKeyboardDevice* keyboard_; 115 | 116 | CSpinLock mutex_; 117 | 118 | TGamePadState gamepad_state_[MAX_GAMEPADS]; 119 | TGamePadState gamepad_state_buffered_[MAX_GAMEPADS]; 120 | unsigned action_buttons_; 121 | 122 | // Keyboard definition 123 | unsigned char keyboard_lines_ [10]; 124 | 125 | static KeyboardPi* this_ptr_; 126 | 127 | std::vector gamepad_list_; 128 | GamepadDef* gamepad_active_[MAX_GAMEPADS]; 129 | bool select_; 130 | 131 | struct RawToCPC 132 | { 133 | unsigned char* line_index; 134 | unsigned char bit; 135 | }; 136 | RawToCPC raw_to_cpc_map_[0x100]; 137 | unsigned char old_raw_keys_[6]; 138 | unsigned char old_modifier_; 139 | }; -------------------------------------------------------------------------------- /src/ScreenMenu.cpp: -------------------------------------------------------------------------------- 1 | // 2 | #include "ScreenMenu.h" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "res/button_1.h" 10 | #include "res/coolspot.h" 11 | 12 | #define MAX_ITEM_PER_PAGE 10 13 | #define MOVE_BASE 7 14 | 15 | #define DRIVE "SD:" 16 | 17 | #define PATH_CARTIRDGE "SD:/CART" 18 | #define PATH_DISK "SD:/DISK" 19 | #define PATH_TAPE "SD:/TAPE" 20 | 21 | #define PATH_QUICK_SNA "SD:/quick.sna" 22 | 23 | 24 | MainMenuWindows::MainMenuWindows (DisplayPi* display) : Windows (display) 25 | { 26 | // Create Title bitmap 27 | // todo 28 | 29 | // Create inner menu 30 | menu_ = new MenuWindows (display); 31 | menu_->CreateWindow ( this, 240, 70, 1000, 800); 32 | } 33 | 34 | MainMenuWindows::~MainMenuWindows () 35 | { 36 | delete menu_; 37 | } 38 | 39 | void MainMenuWindows::ResetMenu() 40 | { 41 | // Set focus to first item 42 | menu_->SetFocus(0); 43 | } 44 | 45 | ScreenMenu::MenuItem base_menu[] = 46 | { 47 | { "Resume", &ScreenMenu::Resume}, 48 | { "Insert Cartridge", &ScreenMenu::InsertCartridge}, 49 | { "Insert Disk", &ScreenMenu::InsertDisk}, 50 | { "Insert Tape", &ScreenMenu::InsertTape}, 51 | { "SugarPi Setup", &ScreenMenu::SugarSetup}, 52 | /* { "Hardware Setup", &ScreenMenu::HardwareSetup}, 53 | { "Quick Save", &ScreenMenu::Save}, 54 | { "Quick Load", &ScreenMenu::Load},*/ 55 | { "Reset", &ScreenMenu::Reset}, 56 | { "Shutdown", &ScreenMenu::ShutDown}, 57 | { nullptr, nullptr} 58 | }; 59 | 60 | ScreenMenu::ScreenMenu(ILog* log, CLogger* logger, DisplayPi* display, SoundMixer* sound_mixer, KeyboardPi* keyboard, Motherboard* motherboard, SugarPiSetup* setup) : 61 | logger_(logger), 62 | display_(display), 63 | sound_mixer_(sound_mixer), 64 | keyboard_(keyboard), 65 | setup_(setup), 66 | motherboard_(motherboard), 67 | snapshot_(nullptr) 68 | { 69 | snapshot_ = new CSnapshot(log); 70 | snapshot_->SetMachine(motherboard_); 71 | ///////////////////////////////////////////////// 72 | // Windows creation 73 | 74 | // Create Main window menu 75 | unsigned int i = 0; 76 | main_menu_ = new MainMenuWindows (display_); 77 | while (base_menu[i].label_ != nullptr && i < MAX_ITEM_PER_PAGE) 78 | { 79 | // Display menu bitmap 80 | main_menu_->GetMenu()->AddMenuItem(base_menu[i].label_, new ActionMenu(this, base_menu[i].function)); 81 | 82 | i++; 83 | } 84 | 85 | } 86 | 87 | ScreenMenu::~ScreenMenu() 88 | { 89 | delete snapshot_; 90 | delete main_menu_; 91 | } 92 | 93 | IAction::ActionReturn ScreenMenu::SetSync(bool* value) 94 | { 95 | setup_->SetSync (*value ? SugarPiSetup::SYNC_FRAME:SugarPiSetup::SYNC_SOUND); 96 | setup_->Save(); 97 | return IAction::Action_Update; 98 | } 99 | 100 | IAction::ActionReturn ScreenMenu::Back() 101 | { 102 | return IAction::Action_Back; 103 | } 104 | 105 | IAction::ActionReturn ScreenMenu::Resume() 106 | { 107 | return IAction::Action_QuitMenu; 108 | } 109 | 110 | IAction::ActionReturn ScreenMenu::LoadCartridge( const char* path) 111 | { 112 | CString fullpath = PATH_CARTIRDGE; 113 | fullpath.Append( "/" ); 114 | fullpath.Append( path); 115 | logger_->Write("Menu", LogNotice, "Load cartridge fullpath : %s", (const char*)fullpath); 116 | setup_->LoadCartridge (fullpath); 117 | setup_->Save(); 118 | logger_->Write("Cartridge", LogNotice, "file loaded.Exiting menu"); 119 | 120 | motherboard_->OnOff(); 121 | return IAction::Action_QuitMenu; 122 | } 123 | IAction::ActionReturn ScreenMenu::LoadDisk( const char* path) 124 | { 125 | CString fullpath = PATH_DISK; 126 | fullpath.Append( "/" ); 127 | fullpath.Append( path); 128 | logger_->Write("Menu", LogNotice, "Load Disk fullpath : %s", (const char*)fullpath); 129 | 130 | motherboard_->GetFDC()->LoadDisk(0, fullpath, false); 131 | 132 | setup_->Save(); 133 | logger_->Write("Disk", LogNotice, "file loaded. Exiting menu"); 134 | 135 | motherboard_->OnOff(); 136 | return IAction::Action_QuitMenu; 137 | } 138 | 139 | IAction::ActionReturn ScreenMenu::LoadTape( const char* path) 140 | { 141 | CString fullpath = PATH_TAPE; 142 | fullpath.Append( "/" ); 143 | fullpath.Append( path); 144 | logger_->Write("Menu", LogNotice, "Load Tape fullpath : %s", (const char*)fullpath); 145 | motherboard_->GetTape()->InsertTape (fullpath); 146 | setup_->Save(); 147 | logger_->Write("Tape", LogNotice, "file loaded. Exiting menu"); 148 | 149 | motherboard_->OnOff(); 150 | return IAction::Action_QuitMenu; 151 | } 152 | 153 | IAction::ActionReturn ScreenMenu::InsertMedia(const char* path, IAction::ActionReturn (ScreenMenu::* load_action)(const char*)) 154 | { 155 | DIR Directory; 156 | FILINFO *FileInfo = new FILINFO; 157 | FRESULT Result = f_findfirst(&Directory, FileInfo, path, "*"); 158 | std::vector cartridge_list(20); 159 | 160 | int limit = 0; 161 | // Create menu 162 | unsigned int i = 0; 163 | for (i = 0; Result == FR_OK && FileInfo->fname[0] ; i++) 164 | { 165 | limit++; 166 | if (!(FileInfo->fattrib & (AM_HID | AM_SYS))) 167 | { 168 | logger_->Write("Menu", LogNotice, "Added next %s", FileInfo->fname); 169 | cartridge_list.push_back(FileInfo); 170 | } 171 | FileInfo = new FILINFO; 172 | Result = f_findnext(&Directory, FileInfo); 173 | } 174 | 175 | // Alphabetical Order 176 | FILINFO** array_ordered = new FILINFO* [cartridge_list.size()]; 177 | unsigned int nb_file_ordered = 0; 178 | 179 | for (auto& it:cartridge_list) 180 | { 181 | unsigned int place = nb_file_ordered; 182 | // find right place 183 | for (unsigned int i = 0; i < nb_file_ordered && place == nb_file_ordered; i++) 184 | { 185 | if ( stricmp (it->fname, array_ordered[i]->fname)< 0) 186 | { 187 | place = i; 188 | } 189 | } 190 | // insert it : 191 | // Move everything after place 192 | if ( place != nb_file_ordered) 193 | { 194 | for (unsigned int i = 0; i < nb_file_ordered - place; i++) 195 | { 196 | array_ordered [nb_file_ordered - i] = array_ordered [nb_file_ordered - 1 - i]; 197 | } 198 | } 199 | // insert new item 200 | array_ordered[place] = it; 201 | logger_->Write("Menu", LogNotice, "Added %s, here : %i", it->fname, place); 202 | nb_file_ordered++; 203 | } 204 | 205 | // Create selection menu 206 | Windows* focus = Windows::GetFocus(); 207 | 208 | MainMenuWindows* file_menu = new MainMenuWindows (display_); 209 | 210 | file_menu->GetMenu()->AddMenuItem("..", new ActionMenu( this, &ScreenMenu::Back) ); 211 | 212 | for (unsigned int i = 0; i < nb_file_ordered; i++) 213 | { 214 | // Display menu bitmap 215 | logger_->Write("Menu", LogNotice, "Added %s to menu", array_ordered[i]->fname); 216 | file_menu->GetMenu()->AddMenuItem(array_ordered[i]->fname, new ActionMenuWithParameter(this, load_action, array_ordered[i]->fname) ); 217 | 218 | i++; 219 | } 220 | file_menu->ResetMenu (); 221 | 222 | IAction::ActionReturn return_value = file_menu->DoScreen (this); 223 | logger_->Write("Menu", LogNotice, "file_menu->DoScreen : %i", return_value); 224 | 225 | delete file_menu; 226 | delete [] array_ordered; 227 | for (auto& it:cartridge_list) 228 | { 229 | delete it; 230 | } 231 | Windows::SetFocus(focus); 232 | main_menu_->Invalidate (); 233 | 234 | logger_->Write("Menu", LogNotice, "Return from InsertMedia : %i", return_value); 235 | return return_value; 236 | } 237 | 238 | IAction::ActionReturn ScreenMenu::InsertCartridge() 239 | { 240 | // List cartridge available 241 | // Show contents of root directory 242 | return InsertMedia (PATH_CARTIRDGE, &ScreenMenu::LoadCartridge); 243 | } 244 | 245 | IAction::ActionReturn ScreenMenu::InsertDisk() 246 | { 247 | // List cartridge available 248 | // Show contents of root directory 249 | return InsertMedia (PATH_CARTIRDGE, &ScreenMenu::LoadDisk); 250 | } 251 | 252 | IAction::ActionReturn ScreenMenu::InsertTape() 253 | { 254 | // List cartridge available 255 | // Show contents of root directory 256 | return InsertMedia (PATH_CARTIRDGE, &ScreenMenu::LoadTape); 257 | } 258 | 259 | IAction::ActionReturn ScreenMenu::SugarSetup() 260 | { 261 | Windows* focus = Windows::GetFocus(); 262 | MainMenuWindows* setup_menu = new MainMenuWindows (display_); 263 | 264 | setup_menu->GetMenu()->AddMenuItem("..", new ActionMenu( this, &ScreenMenu::Back) ); 265 | 266 | // Add Synchro menu 267 | bool sync = display_-> IsSyncOnFrame(); 268 | setup_menu->GetMenu()->AddCheckMenuItem ( "Set synchro on Frame", &sync, new ActionMenuWithParameter(this, &ScreenMenu::SetSync, &sync)); 269 | 270 | setup_menu->ResetMenu (); 271 | IAction::ActionReturn return_value = setup_menu->DoScreen(this); 272 | delete setup_menu; 273 | 274 | Windows::SetFocus(focus); 275 | main_menu_->Invalidate (); 276 | 277 | return return_value; 278 | } 279 | 280 | IAction::ActionReturn ScreenMenu::HardwareSetup() 281 | { 282 | logger_->Write("Menu", LogNotice, "ACTION : Select setup"); 283 | return IAction::Action_None; 284 | } 285 | 286 | IAction::ActionReturn ScreenMenu::Save() 287 | { 288 | snapshot_->SaveSnapshot(PATH_QUICK_SNA); 289 | return IAction::Action_QuitMenu; 290 | } 291 | 292 | IAction::ActionReturn ScreenMenu::Load() 293 | { 294 | snapshot_->LoadSnapshot(PATH_QUICK_SNA); 295 | return IAction::Action_QuitMenu; 296 | } 297 | 298 | IAction::ActionReturn ScreenMenu::Reset() 299 | { 300 | motherboard_->OnOff(); 301 | return IAction::Action_QuitMenu; 302 | } 303 | 304 | IAction::ActionReturn ScreenMenu::ShutDown() 305 | { 306 | logger_->Write("Menu", LogNotice, "ACTION : SHUTDOWN"); 307 | return IAction::Action_QuitMenu; 308 | } 309 | 310 | IEvent::Event ScreenMenu::GetEvent () 311 | { 312 | IEvent::Event event = IEvent::NONE; 313 | 314 | if (keyboard_->IsDown()) 315 | { 316 | event = IEvent::DOWN; 317 | } 318 | if (keyboard_->IsUp()) 319 | { 320 | event = IEvent::UP; 321 | } 322 | if (keyboard_->IsAction()) 323 | { 324 | event = IEvent::SELECT; 325 | } 326 | return event; 327 | } 328 | 329 | IAction::ActionReturn ScreenMenu::Handle() 330 | { 331 | logger_->Write("Menu", LogNotice, "MENU ENTER"); 332 | 333 | IAction::ActionReturn action = IAction::Action_None; 334 | keyboard_->ClearBuffer(); 335 | display_->SetFullResolution(true); 336 | 337 | // Wait till next vsync 338 | display_->VSync(); 339 | 340 | // Reset menu 341 | logger_->Write("Menu", LogNotice, "Reset menu..."); 342 | main_menu_->ResetMenu (); 343 | 344 | // Display menu 345 | logger_->Write("Menu", LogNotice, "Do screen..."); 346 | main_menu_->DoScreen (this); 347 | 348 | logger_->Write("Menu", LogNotice, "MENU EXITING !"); 349 | display_->SetFullResolution(false); 350 | display_->VSync(); 351 | 352 | return action; 353 | } 354 | -------------------------------------------------------------------------------- /src/ScreenMenu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "CPCCore/CPCCoreEmu/simple_vector.hpp" 7 | #include "CPCCore/CPCCoreEmu/Motherboard.h" 8 | #include "CPCCore/CPCCoreEmu/Snapshot.h" 9 | #include "CPCCore/CPCCoreEmu/SoundMixer.h" 10 | 11 | #include "DisplayPi.h" 12 | #include "KeyboardPi.h" 13 | #include "SugarPiSetup.h" 14 | #include "Windows.h" 15 | 16 | #define MAX_LANGUAGE 1 17 | 18 | #pragma pack(push, 1) 19 | 20 | class CoolspotFont; 21 | 22 | 23 | 24 | class MainMenuWindows : public Windows 25 | { 26 | public: 27 | MainMenuWindows (DisplayPi* display); 28 | virtual ~MainMenuWindows (); 29 | 30 | void ResetMenu(); 31 | MenuWindows* GetMenu(){return menu_;}; 32 | 33 | protected: 34 | MenuWindows* menu_; 35 | 36 | }; 37 | 38 | class ScreenMenu : public IEvent 39 | { 40 | 41 | public: 42 | 43 | ScreenMenu(ILog* log, CLogger* logger, DisplayPi* display, SoundMixer* sound_mixer, KeyboardPi* keyboard, Motherboard* motherboard, SugarPiSetup* setup); 44 | virtual ~ScreenMenu(); 45 | 46 | IEvent::Event GetEvent (); 47 | 48 | 49 | IAction::ActionReturn Handle(); 50 | 51 | IAction::ActionReturn Back(); 52 | IAction::ActionReturn HardwareSetup(); 53 | IAction::ActionReturn InsertCartridge(); 54 | IAction::ActionReturn InsertDisk(); 55 | IAction::ActionReturn InsertTape(); 56 | IAction::ActionReturn Load(); 57 | IAction::ActionReturn LoadCartridge ( const char* path); 58 | IAction::ActionReturn LoadDisk ( const char* path); 59 | IAction::ActionReturn LoadTape ( const char* path); 60 | IAction::ActionReturn Reset(); 61 | IAction::ActionReturn Resume(); 62 | IAction::ActionReturn Save(); 63 | IAction::ActionReturn SetSync(bool* value); 64 | IAction::ActionReturn ShutDown(); 65 | IAction::ActionReturn SugarSetup(); 66 | 67 | IAction::ActionReturn InsertMedia(const char* path, IAction::ActionReturn (ScreenMenu::* load_action)(const char*)); 68 | 69 | 70 | class MenuItem 71 | { 72 | public: 73 | const char* label_; 74 | //Func function_; 75 | IAction::ActionReturn (ScreenMenu::* function)(); 76 | }; 77 | 78 | protected: 79 | 80 | CLogger* logger_; 81 | DisplayPi* display_; 82 | SoundMixer* sound_mixer_; 83 | KeyboardPi* keyboard_; 84 | SugarPiSetup* setup_; 85 | 86 | Motherboard* motherboard_; 87 | 88 | // Pending actions 89 | CSnapshot* snapshot_; 90 | MainMenuWindows* main_menu_; 91 | }; 92 | 93 | class ActionMenu : public IAction 94 | { 95 | public: 96 | ActionMenu (ScreenMenu* menu, IAction::ActionReturn (ScreenMenu::*pfnAction)()): menu_(menu), pfnAction_(pfnAction) 97 | {}; 98 | 99 | IAction::ActionReturn DoAction() 100 | { 101 | return (menu_->*pfnAction_)(); 102 | }; 103 | 104 | protected: 105 | ScreenMenu* menu_; 106 | IAction::ActionReturn (ScreenMenu::* pfnAction_)(); 107 | }; 108 | 109 | template 110 | class ActionMenuWithParameter : public IAction 111 | { 112 | public: 113 | ActionMenuWithParameter(ScreenMenu* menu, IAction::ActionReturn (ScreenMenu::*pfnAction)(T), T param) : menu_(menu), pfnAction_(pfnAction), parameter_(param) 114 | {}; 115 | 116 | IAction::ActionReturn DoAction() 117 | { 118 | return (menu_->*pfnAction_)(parameter_); 119 | }; 120 | 121 | protected: 122 | ScreenMenu* menu_; 123 | IAction::ActionReturn (ScreenMenu::* pfnAction_)(T); 124 | T parameter_; 125 | }; 126 | 127 | 128 | #pragma pack(pop) -------------------------------------------------------------------------------- /src/SoundPi.cpp: -------------------------------------------------------------------------------- 1 | // 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "SoundPi.h" 8 | 9 | #define SOUND_CHANNELS 2 10 | #define SOUND_BITS 16 11 | #define SOUND_RATE 44100 12 | 13 | #define QUEUE_SIZE_MSECS 100 14 | #define SAMPLE_RATE 44100 15 | #define CHUNK_SIZE 4000 16 | #define WRITE_CHANNELS 2 17 | 18 | #ifdef USE_VCHIQ_SOUND 19 | SoundPi::SoundPi(CLogger* logger, CVCHIQDevice* vchiq_device, CScheduler* scheduler): 20 | #else 21 | SoundPi::SoundPi(CLogger* logger, CInterruptSystem* interrupt, CScheduler* scheduler): 22 | #endif 23 | logger_(logger), 24 | scheduler_(scheduler), 25 | #ifdef USE_VCHIQ_SOUND 26 | vchiq_device_(vchiq_device), 27 | #else 28 | interrupt_(interrupt), 29 | #endif 30 | sound_device_(nullptr), 31 | queue_size_frames_(0), 32 | started_(false), 33 | buffer_(nullptr), 34 | chunk_buffer(nullptr), 35 | index_write_(0), 36 | index_output_(0) 37 | { 38 | //logger_->Write("Sound", LogNotice, "Sound Init"); 39 | //logger_->Write("Sound", LogNotice, "CVCHIQSoundBaseDevice created"); 40 | 41 | // 1/100e second buffer 42 | for (int i = 0; i < QUEUE_SIZE; i++) 43 | { 44 | data_[i].buffer_length_ = SOUND_BITS / 8 * SOUND_CHANNELS * SOUND_RATE / 100; 45 | data_[i].data_ = new char[SOUND_BITS / 8 * SOUND_CHANNELS * SOUND_RATE / 100]; 46 | data_[i].status_ = IWaveHDR::UNUSED; 47 | } 48 | chunk_buffer = new unsigned char[SOUND_BITS / 8 * SOUND_CHANNELS * SOUND_RATE / 10]; 49 | } 50 | 51 | SoundPi::~SoundPi() 52 | { 53 | for (int i = 0; i < QUEUE_SIZE; i++) 54 | { 55 | delete []data_[i].data_ ; 56 | } 57 | delete sound_device_; 58 | delete chunk_buffer; 59 | } 60 | 61 | void SoundPi::Initialize() 62 | { 63 | #ifdef USE_VCHIQ_SOUND 64 | sound_device_ = new CVCHIQSoundBaseDevice(vchiq_device_, SOUND_RATE, CHUNK_SIZE, VCHIQSoundDestinationAuto); 65 | #else 66 | sound_device_ = new CPWMSoundBaseDevice (interrupt_, SOUND_RATE, CHUNK_SIZE); 67 | #endif 68 | 69 | if (!sound_device_->AllocateQueue(QUEUE_SIZE_MSECS)) 70 | { 71 | //logger_->Write ("Sound", LogPanic, "Cannot allocate sound queue"); 72 | } 73 | sound_device_->SetWriteFormat(SoundFormatSigned16, WRITE_CHANNELS); 74 | queue_size_frames_ = sound_device_->GetQueueSizeFrames(); 75 | //logger_->Write("Sound", LogNotice, "queue_size_frames_ : %i", queue_size_frames_); 76 | //logger_->Write("Sound", LogNotice, "GetRangeMin : %i", sound_device_->GetRangeMin()); 77 | //logger_->Write("Sound", LogNotice, "GetRangeMax : %i", sound_device_->GetRangeMax()); 78 | 79 | 80 | //logger_->Write("Sound", LogNotice, "Init done"); 81 | } 82 | 83 | unsigned int SoundPi::GetMaxValue() 84 | { 85 | return sound_device_->GetRangeMax(); 86 | } 87 | 88 | unsigned int SoundPi::GetMinValue() 89 | { 90 | return sound_device_->GetRangeMin(); 91 | } 92 | 93 | 94 | bool SoundPi::Init(int sample_rate, int sample_bits, int nb_channels) 95 | { 96 | //logger_->Write("Sound", LogNotice, "Init"); 97 | return true; 98 | } 99 | 100 | void SoundPi::Reinit() 101 | { 102 | } 103 | unsigned int SoundPi::GetSampleRate() 104 | { 105 | return SOUND_RATE; 106 | } 107 | 108 | unsigned int SoundPi::GetBitDepth() 109 | { 110 | //logger_->Write("Sound", LogNotice, "GetBitDepth"); 111 | return SOUND_BITS; 112 | } 113 | 114 | unsigned int SoundPi::GetNbChannels() 115 | { 116 | //logger_->Write("Sound", LogNotice, "GetNbChannels"); 117 | return SOUND_CHANNELS; 118 | } 119 | 120 | void SoundPi::CheckBuffersStatus() 121 | { 122 | } 123 | 124 | IWaveHDR* SoundPi::GetFreeBuffer() 125 | { 126 | for (int i = 0; i < QUEUE_SIZE; i++) 127 | { 128 | if (data_[i].status_ == IWaveHDR::UNUSED) 129 | { 130 | ////logger_->Write("Sound", LogNotice, "Free buffer found : %i", i); 131 | data_[i].status_ = IWaveHDR::USED; 132 | return &data_[i]; 133 | } 134 | } 135 | return nullptr; 136 | } 137 | 138 | void SoundPi::AddBufferToPlay(IWaveHDR* wav) 139 | { 140 | int lenght_written = 0; 141 | while ( lenght_written != wav->buffer_length_) 142 | { 143 | lenght_written += sound_device_->Write(&wav->data_[ lenght_written], wav->buffer_length_- lenght_written); 144 | scheduler_->Yield(); 145 | } 146 | if (!started_) 147 | { 148 | started_ = true; 149 | if (!sound_device_->Start()) 150 | { 151 | logger_->Write("Sound", LogPanic, "Cannot start sound device"); 152 | } 153 | } 154 | wav->status_ = IWaveHDR::UNUSED; 155 | } 156 | 157 | unsigned SoundPi::GetChunk(s16 *pBuffer, unsigned nChunkSize) 158 | { 159 | memcpy(pBuffer, &chunk_buffer[index_output_], nChunkSize); 160 | index_output_ += nChunkSize; 161 | return 0; 162 | } 163 | 164 | -------------------------------------------------------------------------------- /src/SoundPi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "CPCCore/CPCCoreEmu/ISound.h" 12 | 13 | 14 | #define QUEUE_SIZE 8 15 | 16 | class SoundPi : public ISound 17 | { 18 | public: 19 | #ifdef USE_VCHIQ_SOUND 20 | SoundPi(CLogger* logger, CVCHIQDevice* vchiq_device, CScheduler* scheduler); 21 | #else 22 | SoundPi(CLogger* logger, CInterruptSystem* interrupt, CScheduler* scheduler); 23 | #endif 24 | virtual ~SoundPi(); 25 | 26 | virtual void SetDefaultConfiguration() {} 27 | virtual void SaveConfiguration(const char* config_name, const char* ini_file) {} 28 | virtual bool LoadConfiguration(const char* config_name, const char* ini_file) { return true; } 29 | 30 | virtual bool Init(int sample_rate, int sample_bits, int nb_channels); 31 | virtual void Reinit(); 32 | virtual unsigned int GetMaxValue() ; 33 | virtual unsigned int GetMinValue() ; 34 | virtual unsigned int GetSampleRate(); 35 | virtual unsigned int GetBitDepth(); 36 | virtual unsigned int GetNbChannels(); 37 | virtual void CheckBuffersStatus(); 38 | 39 | virtual IWaveHDR* GetFreeBuffer(); 40 | virtual void AddBufferToPlay(IWaveHDR*); 41 | 42 | virtual void SyncWithSound() {}; 43 | 44 | void Initialize(); 45 | 46 | virtual unsigned GetChunk(s16 *pBuffer, unsigned nChunkSize); 47 | 48 | protected: 49 | CLogger* logger_; 50 | CScheduler* scheduler_; 51 | 52 | #ifdef USE_VCHIQ_SOUND 53 | CVCHIQDevice* vchiq_device_; 54 | CVCHIQSoundBaseDevice*sound_device_; 55 | #else 56 | CPWMSoundBaseDevice * sound_device_; 57 | CInterruptSystem* interrupt_; 58 | #endif 59 | 60 | unsigned queue_size_frames_; 61 | bool started_; 62 | char* buffer_; 63 | IWaveHDR data_[QUEUE_SIZE]; 64 | 65 | 66 | // Output buffer 67 | unsigned char* chunk_buffer; 68 | unsigned int index_write_; 69 | unsigned int index_output_; 70 | }; -------------------------------------------------------------------------------- /src/SugarPiSetup.cpp: -------------------------------------------------------------------------------- 1 | // 2 | #include "SugarPiSetup.h" 3 | #include 4 | 5 | #define DRIVE "SD:" 6 | 7 | #define SECTION_SETUP "SETUP" 8 | #define KEY_SYNC "sync" 9 | #define KEY_CART "cart" 10 | #define KEY_LAYOUT "layout" 11 | 12 | #define KEY_SYNC_SOUND "sound" 13 | #define KEY_SYNC_FRAME "frame" 14 | 15 | #define DEFAULT_CART "SD:/CART/crtc3_projo.cpr" 16 | #define DEFAULT_LAYOUT "SD:/LAYOUT/101_keyboard" 17 | 18 | SugarPiSetup::SugarPiSetup( CLogger* log) : log_(log), display_(nullptr), sound_(nullptr), motherboard_(nullptr), keyboard_(nullptr) 19 | { 20 | config_ = new ConfigurationManager(log); 21 | } 22 | 23 | SugarPiSetup::~SugarPiSetup() 24 | { 25 | delete config_; 26 | } 27 | 28 | void SugarPiSetup::Init(DisplayPi* display, SoundMixer* sound, Motherboard *motherboard, KeyboardPi* keyboard) 29 | { 30 | display_ = display; 31 | sound_ = sound; 32 | motherboard_ = motherboard; 33 | keyboard_ = keyboard; 34 | } 35 | 36 | void SugarPiSetup::Load() 37 | { 38 | config_->OpenFile(DRIVE "/Config/config"); 39 | 40 | // Syncronisation 41 | #define SIZE_OF_BUFFER 256 42 | char buffer[SIZE_OF_BUFFER]; 43 | if (config_->GetConfiguration (SECTION_SETUP, KEY_SYNC, KEY_SYNC_SOUND, buffer, SIZE_OF_BUFFER )) 44 | { 45 | if (strcmp ( buffer, KEY_SYNC_SOUND) == 0) 46 | { 47 | SetSync(SYNC_SOUND); 48 | } 49 | else if (strcmp ( buffer, KEY_SYNC_FRAME) == 0) 50 | { 51 | SetSync(SYNC_FRAME); 52 | } 53 | } 54 | 55 | // Keyboard layout (if any) 56 | if (config_->GetConfiguration (SECTION_SETUP, KEY_LAYOUT, DEFAULT_LAYOUT, buffer, SIZE_OF_BUFFER )) 57 | { 58 | keyboard_->LoadKeyboardLayout (buffer); 59 | } 60 | // todo 61 | 62 | // Hardware configuration 63 | 64 | // Current cartridge 65 | if (config_->GetConfiguration (SECTION_SETUP, KEY_CART, DEFAULT_CART, buffer, SIZE_OF_BUFFER )) 66 | { 67 | LoadCartridge(buffer); 68 | } 69 | } 70 | 71 | void SugarPiSetup::Save() 72 | { 73 | // Hardware configuration 74 | // to add 75 | 76 | // Syncronisation 77 | config_->SetConfiguration (SECTION_SETUP, KEY_SYNC, (sync_==SYNC_SOUND)?KEY_SYNC_SOUND:KEY_SYNC_FRAME); 78 | 79 | // Current cartridge 80 | config_->SetConfiguration (SECTION_SETUP, KEY_CART, cart_path_.c_str()); 81 | 82 | config_->CloseFile(); 83 | } 84 | 85 | void SugarPiSetup::SetSync (SYNC_TYPE sync) 86 | { 87 | sync_ = sync; 88 | if (sync==SYNC_SOUND) 89 | { 90 | display_->SyncWithFrame(false); 91 | sound_->SyncOnSound(true); 92 | } 93 | else 94 | { 95 | display_->SyncWithFrame(true); 96 | sound_->SyncOnSound(false); 97 | } 98 | } 99 | 100 | SugarPiSetup::SYNC_TYPE SugarPiSetup::GetSync () 101 | { 102 | return sync_; 103 | } 104 | 105 | void SugarPiSetup::LoadCartridge (const char* path) 106 | { 107 | FIL File; 108 | FRESULT Result = f_open(&File, path, FA_READ | FA_OPEN_EXISTING); 109 | 110 | if (Result != FR_OK) 111 | { 112 | log_->Write("Kernel", LogNotice, "Cannot open file: %s", path); 113 | return; 114 | } 115 | 116 | FILINFO file_info; 117 | f_stat(path, &file_info); 118 | unsigned char* buff = new unsigned char[file_info.fsize]; 119 | unsigned nBytesRead; 120 | 121 | f_read(&File, buff, file_info.fsize, &nBytesRead); 122 | if (file_info.fsize != nBytesRead) 123 | { 124 | log_->Write("Kernel", LogNotice, "Read incorrect %i instead of ", nBytesRead, file_info.fsize); 125 | delete [] buff; 126 | return; 127 | } 128 | 129 | cart_path_ = path; 130 | LoadCprFromBuffer(buff, nBytesRead); 131 | 132 | delete [] buff; 133 | } 134 | 135 | int SugarPiSetup::LoadCprFromBuffer(unsigned char* buffer, int size) 136 | { 137 | // Check RIFF chunk 138 | int index = 0; 139 | if (size >= 12 140 | && (memcmp(&buffer[0], "RIFF", 4) == 0) 141 | && (memcmp(&buffer[8], "AMS!", 4) == 0) 142 | ) 143 | { 144 | // Reinit Cartridge 145 | motherboard_->EjectCartridge(); 146 | 147 | // Ok, it's correct. 148 | index += 4; 149 | // Check the whole size ? 150 | index += 8; 151 | 152 | // 'fmt ' chunk ? skip it 153 | if (index + 8 < size && (memcmp(&buffer[index], "fmt ", 4) == 0)) 154 | { 155 | index += 8; 156 | } 157 | 158 | // Good. 159 | // Now we are at the first cbxx 160 | while (index + 8 < size) 161 | { 162 | if (buffer[index] == 'c' && buffer[index + 1] == 'b') 163 | { 164 | index += 2; 165 | char buffer_block_number[3] = { 0 }; 166 | memcpy(buffer_block_number, &buffer[index], 2); 167 | int block_number = (buffer_block_number[0] - '0') * 10 + (buffer_block_number[1] - '0'); 168 | index += 2; 169 | 170 | // Read size 171 | int block_size = buffer[index] 172 | + (buffer[index + 1] << 8) 173 | + (buffer[index + 2] << 16) 174 | + (buffer[index + 3] << 24); 175 | index += 4; 176 | 177 | if (block_size <= size && block_number < 256) 178 | { 179 | // Copy datas to proper ROM 180 | unsigned char* rom = motherboard_->GetCartridge(block_number); 181 | memset(rom, 0, 0x1000); 182 | memcpy(rom, &buffer[index], block_size); 183 | index += block_size; 184 | } 185 | else 186 | { 187 | return -1; 188 | } 189 | } 190 | else 191 | { 192 | return -1; 193 | } 194 | } 195 | } 196 | else 197 | { 198 | // Incorrect headers 199 | return -1; 200 | } 201 | 202 | return 0; 203 | } -------------------------------------------------------------------------------- /src/SugarPiSetup.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "DisplayPi.h" 6 | #include "KeyboardPi.h" 7 | #include "CPCCore/CPCCoreEmu/Motherboard.h" 8 | #include "CPCCore/CPCCoreEmu/SoundMixer.h" 9 | #include "ConfigurationManager.h" 10 | 11 | class SugarPiSetup 12 | { 13 | public : 14 | SugarPiSetup ( CLogger* log); 15 | virtual ~SugarPiSetup(); 16 | 17 | void Init(DisplayPi* display, SoundMixer* sound, Motherboard *motherboard, KeyboardPi* keyboard); 18 | 19 | void Load(); 20 | void Save(); 21 | 22 | // Access each values 23 | enum SYNC_TYPE{ 24 | SYNC_FRAME, 25 | SYNC_SOUND 26 | } ; 27 | void SetSync (SYNC_TYPE sync); 28 | SYNC_TYPE GetSync (); 29 | 30 | void LoadCartridge (const char* path); 31 | void LoadDisk (const char* path); 32 | void LoadTape (const char* path); 33 | 34 | protected: 35 | 36 | int LoadCprFromBuffer(unsigned char* buffer, int size); 37 | 38 | 39 | CLogger* log_; 40 | DisplayPi* display_; 41 | SoundMixer* sound_; 42 | Motherboard* motherboard_; 43 | KeyboardPi* keyboard_; 44 | 45 | ConfigurationManager* config_; 46 | 47 | SYNC_TYPE sync_; 48 | std::string cart_path_; 49 | }; -------------------------------------------------------------------------------- /src/Windows.cpp: -------------------------------------------------------------------------------- 1 | // 2 | #include 3 | 4 | #include 5 | 6 | #include "Windows.h" 7 | 8 | Windows* Windows::focus_ = nullptr; 9 | 10 | //////////////////////////////////////////////////////////////////////////////////// 11 | Windows::Windows(DisplayPi* display) : display_(display), x_(0), y_(0), width_(0), height_(0), parent_(nullptr), windows_children_(nullptr) 12 | { 13 | } 14 | 15 | Windows::~Windows() 16 | { 17 | } 18 | 19 | void Windows::CreateWindow (Windows* parent, int x, int y, unsigned int width, unsigned int height) 20 | { 21 | parent_ = parent; 22 | x_ = x; 23 | y_ = y; 24 | width_ = width; 25 | height_ = height; 26 | 27 | if ( parent_ != nullptr) 28 | { 29 | parent_->AddChild(this); 30 | } 31 | } 32 | 33 | void Windows::Clear() 34 | { 35 | CLogger::Get ()->Write ("Windows", LogNotice,"Clear windows" ); 36 | // Background 37 | for (int i = 0; i < display_->GetHeight(); i++) 38 | { 39 | int* line = display_->GetVideoBuffer(i); 40 | memset(line, 0x0, sizeof(int) * display_->GetWidth()); 41 | } 42 | 43 | } 44 | 45 | void Windows::AddChild(Windows* child) 46 | { 47 | WindowsQueue** current_queue = &windows_children_; 48 | CLogger::Get ()->Write ("Windows", LogNotice,"windows_children_ : %i", *current_queue); 49 | while ( *current_queue != nullptr) 50 | { 51 | CLogger::Get ()->Write ("Windows", LogNotice,"current_queue not null : %i", *current_queue); 52 | current_queue = &((*current_queue)->next_); 53 | } 54 | 55 | CLogger::Get ()->Write ("Windows", LogNotice,"current_queue final : %i", *current_queue); 56 | *current_queue = new WindowsQueue; 57 | (*current_queue)->wnd_ = child; 58 | (*current_queue)->next_ = nullptr; 59 | 60 | CLogger::Get ()->Write ("Windows", LogNotice,"Final windows_children_ : %i", *current_queue); 61 | } 62 | 63 | void Windows::WindowsToDisplay(int& x, int& y) 64 | { 65 | Windows* wnd = this; 66 | while ( wnd->parent_ != nullptr) 67 | { 68 | wnd = wnd->parent_; 69 | x += wnd->x_; 70 | y += wnd->y_; 71 | } 72 | } 73 | 74 | void Windows::RedrawWindow () 75 | { 76 | CLogger::Get ()->Write ("Windows", LogNotice,"Windows::RedrawWindow"); 77 | } 78 | 79 | void Windows::RedrawChildren () 80 | { 81 | WindowsQueue** current_queue = &windows_children_; 82 | CLogger::Get ()->Write ("Windows", LogNotice,"windows_children_ : %i", *current_queue); 83 | while ( *current_queue != nullptr) 84 | { 85 | CLogger::Get ()->Write ("Windows", LogNotice,"current_queue not null : %i", *current_queue); 86 | (*current_queue)->wnd_->RedrawWindow(); 87 | (*current_queue)->wnd_->RedrawChildren(); 88 | current_queue = &((*current_queue)->next_); 89 | } 90 | 91 | 92 | } 93 | 94 | void Windows::Invalidate () 95 | { 96 | // Clear from top windows 97 | Redraw ( true); 98 | } 99 | 100 | void Windows::Redraw (bool clear) 101 | { 102 | if (clear) 103 | Clear(); 104 | 105 | CLogger::Get ()->Write ("Windows", LogNotice,"Windows::Redraw"); 106 | // Redraw window 107 | RedrawWindow (); 108 | 109 | // Redraw children 110 | RedrawChildren (); 111 | 112 | // Sync 113 | CLogger::Get ()->Write ("Windows", LogNotice,"VSync"); 114 | display_->VSync(); 115 | 116 | } 117 | 118 | IAction::ActionReturn Windows::DoScreen (IEvent* event_handler) 119 | { 120 | CLogger::Get ()->Write ("Windows", LogNotice,"DoScreen"); 121 | 122 | // Redraw the window 123 | Redraw (true); 124 | 125 | // Wait for an event 126 | IAction::ActionReturn exit_function = IAction::Action_None; 127 | while (exit_function == IAction::Action_None) 128 | { 129 | IEvent::Event event = event_handler->GetEvent(); 130 | if (event == IEvent::NONE) 131 | { 132 | // Wait a bit 133 | CTimer::Get ()->MsDelay (10); 134 | } 135 | else 136 | { 137 | // Send it to focused window 138 | IAction::ActionReturn retval = HandleEvent (event); 139 | CLogger::Get ()->Write ("Windows", LogNotice,"HandleEvent return : %i", retval); 140 | switch( retval ) 141 | { 142 | case IAction::Action_Back: 143 | case IAction::Action_QuitMenu: 144 | case IAction::Action_Shutdown: 145 | exit_function = retval; 146 | break; 147 | case IAction::Action_Update: 148 | Redraw (true); 149 | break; 150 | default: 151 | break; 152 | } 153 | } 154 | } 155 | // Back is only meant to exit one menu. 156 | if ( exit_function == IAction::Action_Back) exit_function = IAction::Action_None; 157 | return exit_function; 158 | } 159 | 160 | IAction::ActionReturn Windows::HandleEvent( IEvent::Event event) 161 | { 162 | // try to pass event to focused window 163 | if ( focus_ != nullptr) 164 | { 165 | return focus_->HandleEvent (event); 166 | } 167 | 168 | // Otherwise, nothing to do here... which means we can leave ! 169 | return IAction::ActionReturn::Action_QuitMenu; 170 | } 171 | 172 | void Windows::SetFocus () 173 | { 174 | if ( focus_ != nullptr) 175 | { 176 | CLogger::Get ()->Write ("Windows", LogNotice,"Remove old focus : %i", focus_); 177 | focus_->RemoveFocus (); 178 | } 179 | focus_ = this; 180 | CLogger::Get ()->Write ("Windows", LogNotice,"New focus : %i", focus_); 181 | } 182 | 183 | void Windows::RemoveFocus () 184 | { 185 | } 186 | 187 | //////////////////////////////////////////////////////////////////////////////////// 188 | MenuItemWindows::MenuItemWindows (DisplayPi* display) : Windows(display), action_(nullptr) 189 | { 190 | 191 | } 192 | MenuItemWindows::~MenuItemWindows () 193 | { 194 | 195 | } 196 | 197 | void MenuItemWindows::CreateWindow (const char* label, Windows* parent, int x, int y, unsigned int width, unsigned int height) 198 | { 199 | label_ = label; 200 | Windows::CreateWindow ( parent, x, y, width, height); 201 | } 202 | 203 | void MenuItemWindows::SetAction (IAction* action) 204 | { 205 | action_ = action; 206 | } 207 | 208 | void MenuItemWindows::RedrawWindow ( ) 209 | { 210 | int x = x_ + 15; 211 | int y = y_; 212 | WindowsToDisplay(x, y); 213 | 214 | CLogger::Get ()->Write ("Windows", LogNotice,"Redraw Text %s, x=%i, y=%i, focus = %s", (const char*)label_, x, y, focus_==this?"yes":"no"); 215 | // Focus ? 216 | if (focus_==this) 217 | { 218 | // draw it 219 | display_->DisplayText ("*", x-15, y, focus_==this); 220 | } 221 | display_->DisplayText (label_, x, y, focus_==this); 222 | } 223 | 224 | IAction::ActionReturn MenuItemWindows::HandleEvent( IEvent::Event event) 225 | { 226 | // 227 | switch (event) 228 | { 229 | case IEvent::Event::SELECT: 230 | // Action ! 231 | if (action_ != nullptr) 232 | { 233 | CLogger::Get ()->Write ("Windows", LogNotice,"SELECT pushed : Action is done"); 234 | IAction::ActionReturn ret = action_->DoAction () ; 235 | CLogger::Get ()->Write ("Windows", LogNotice,"HandleEvent ITEM => %i", ret); 236 | return ret; 237 | } 238 | break; 239 | default: 240 | if ( parent_ != nullptr) 241 | return parent_->HandleEvent(event); 242 | } 243 | 244 | return IAction::ActionReturn::Action_None; 245 | } 246 | 247 | //////////////////////////////////////////////////////////////////////////////////// 248 | CheckMenuItemWindows::CheckMenuItemWindows (DisplayPi* display) : MenuItemWindows(display), value_(nullptr) 249 | { 250 | 251 | } 252 | CheckMenuItemWindows::~CheckMenuItemWindows () 253 | { 254 | 255 | } 256 | 257 | void CheckMenuItemWindows::CreateWindow (const char* label, bool* value, Windows* parent, int x, int y, unsigned int width, unsigned int height) 258 | { 259 | MenuItemWindows::CreateWindow( label, parent, x, y, width, height); 260 | value_ = value; 261 | } 262 | 263 | void CheckMenuItemWindows::RedrawWindow ( ) 264 | { 265 | int x = x_ + 15; 266 | int y = y_; 267 | WindowsToDisplay(x, y); 268 | 269 | CLogger::Get ()->Write ("Windows", LogNotice,"Redraw Text %s, x=%i, y=%i, focus = %s", (const char*)label_, x, y, focus_==this?"yes":"no"); 270 | // Focus ? 271 | if (focus_==this) 272 | { 273 | // draw it 274 | display_->DisplayText ("*", x-15, y, focus_==this); 275 | } 276 | // Draw the check box 277 | display_->DisplayText ((*value_)?"[X]":"[ ]", x, y); 278 | 279 | display_->DisplayText (label_, x+30, y, focus_==this); 280 | 281 | } 282 | 283 | IAction::ActionReturn CheckMenuItemWindows::HandleEvent( IEvent::Event event) 284 | { 285 | // 286 | switch (event) 287 | { 288 | case IEvent::Event::SELECT: 289 | // Action ! 290 | (*value_) = (*value_)?false:true; 291 | CLogger::Get ()->Write ("Windows", LogNotice,"New check value = %s", (*value_)?"YES":"NO"); 292 | if (action_ != nullptr) 293 | { 294 | CLogger::Get ()->Write ("Windows", LogNotice,"SELECT pushed : Action is done"); 295 | return action_->DoAction () ; 296 | } 297 | break; 298 | default: 299 | if ( parent_ != nullptr) 300 | return parent_->HandleEvent(event); 301 | } 302 | 303 | return IAction::ActionReturn::Action_None; 304 | } 305 | //////////////////////////////////////////////////////////////////////////////////// 306 | MenuWindows::MenuWindows (DisplayPi* display) : Windows (display), current_focus_(-1) 307 | { 308 | 309 | } 310 | 311 | MenuWindows::~MenuWindows () 312 | { 313 | // Clear items 314 | for (auto& it:list_item_) 315 | { 316 | delete it; 317 | } 318 | list_item_.clear(); 319 | } 320 | 321 | void MenuWindows::AddMenuItem (const char* label, IAction* action) 322 | { 323 | // Add item to menu 324 | CLogger::Get ()->Write ("Windows", LogNotice,"AddMenuItem %s", (const char*)label); 325 | MenuItemWindows* item = new MenuItemWindows (display_); 326 | item->CreateWindow ( label, this, 10, list_item_.size()*20, 800, 19); 327 | item->SetAction(action); 328 | 329 | list_item_.push_back(item); 330 | 331 | if ( current_focus_ == -1) 332 | current_focus_ = 0; 333 | } 334 | 335 | void MenuWindows::AddCheckMenuItem (const char* label, bool* value, IAction* action) 336 | { 337 | // Add item to menu 338 | CLogger::Get ()->Write ("Windows", LogNotice,"AddMenuItem %s", (const char*)label); 339 | CheckMenuItemWindows* item = new CheckMenuItemWindows (display_); 340 | item->CreateWindow ( label, value, this, 10, list_item_.size()*20, 800, 19); 341 | item->SetAction(action); 342 | 343 | list_item_.push_back(item); 344 | Redraw (true); 345 | 346 | if ( current_focus_ == -1) 347 | current_focus_ = 0; 348 | } 349 | 350 | 351 | void MenuWindows::RedrawWindow () 352 | { 353 | int x = 450; 354 | int y = 47; 355 | WindowsToDisplay(x, y); 356 | CLogger::Get ()->Write ("Windows", LogNotice,"Redraw MenuWindows" ); 357 | display_->DisplayText("SugarPi", x, y); 358 | } 359 | 360 | IAction::ActionReturn MenuWindows::HandleEvent( IEvent::Event event) 361 | { 362 | // do something 363 | switch (event) 364 | { 365 | case IEvent::Event::DOWN: 366 | // Go down in the menu 367 | if (current_focus_ < static_cast(list_item_.size())-1) 368 | { 369 | CLogger::Get ()->Write ("Windows", LogNotice,"DOWN in menu"); 370 | current_focus_++; 371 | list_item_.at(current_focus_)->SetFocus (); 372 | Redraw (true); 373 | } 374 | break; 375 | case IEvent::Event::UP: 376 | // Go up in the menu 377 | if (current_focus_ > 0) 378 | { 379 | CLogger::Get ()->Write ("Windows", LogNotice,"UP in menu"); 380 | current_focus_--; 381 | list_item_.at(current_focus_)->SetFocus (); 382 | Redraw (true); 383 | } 384 | break; 385 | default: 386 | break; 387 | } 388 | 389 | return IAction::ActionReturn::Action_None; 390 | } 391 | 392 | void MenuWindows::SetFocus (unsigned int index) 393 | { 394 | // Set focus to first item 395 | if ( list_item_.size() > index) 396 | { 397 | current_focus_ = index; 398 | list_item_.at(index)->SetFocus (); 399 | } 400 | 401 | } -------------------------------------------------------------------------------- /src/Windows.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | #include 5 | #include 6 | 7 | #include "CPCCore/CPCCoreEmu/simple_vector.hpp" 8 | 9 | #include "DisplayPi.h" 10 | 11 | class IAction 12 | { 13 | public: 14 | enum ActionReturn 15 | { 16 | Action_None, 17 | Action_Update, 18 | Action_QuitMenu, 19 | Action_Back, 20 | Action_Shutdown, 21 | }; 22 | virtual ActionReturn DoAction() = 0; 23 | }; 24 | 25 | class IEvent 26 | { 27 | public: 28 | enum Event 29 | { 30 | NONE, 31 | SELECT, 32 | BACK, 33 | DOWN, 34 | UP, 35 | LEFT, 36 | RIGHT, 37 | }; 38 | 39 | virtual Event GetEvent () = 0; 40 | }; 41 | 42 | class Windows 43 | { 44 | public: 45 | Windows(DisplayPi* display); 46 | virtual ~Windows(); 47 | 48 | virtual IAction::ActionReturn DoScreen (IEvent* event_handler); 49 | 50 | virtual void CreateWindow (Windows* parent, int x, int y, unsigned int width, unsigned int height); 51 | virtual void AddChild(Windows* child); 52 | 53 | void WindowsToDisplay(int& x, int& y); 54 | virtual void Clear(); 55 | virtual void Redraw (bool clear = true); 56 | virtual void RedrawWindow (); 57 | virtual void RedrawChildren (); 58 | 59 | virtual IAction::ActionReturn HandleEvent( IEvent::Event event); 60 | 61 | virtual void SetFocus (); 62 | virtual void RemoveFocus (); 63 | 64 | void Invalidate (); 65 | static Windows* GetFocus() { return focus_;} 66 | static void SetFocus(Windows* focus) { focus_ = focus;} 67 | 68 | protected: 69 | 70 | // Display 71 | DisplayPi* display_; 72 | 73 | // Coordinate 74 | int x_; 75 | int y_; 76 | unsigned int width_; 77 | unsigned int height_; 78 | 79 | // current Focus window 80 | static Windows* focus_; 81 | 82 | // Windows parent & child 83 | Windows* parent_; 84 | 85 | struct WindowsQueue 86 | { 87 | Windows* wnd_; 88 | WindowsQueue* next_; 89 | }; 90 | WindowsQueue* windows_children_; 91 | }; 92 | 93 | class MenuItemWindows : public Windows 94 | { 95 | public: 96 | MenuItemWindows (DisplayPi* display); 97 | virtual ~MenuItemWindows (); 98 | 99 | virtual void CreateWindow (const char* label, Windows* parent, int x, int y, unsigned int width, unsigned int height); 100 | virtual void SetAction (IAction* action); 101 | virtual void RedrawWindow (); 102 | virtual IAction::ActionReturn HandleEvent( IEvent::Event event); 103 | 104 | protected: 105 | CString label_; 106 | IAction* action_; 107 | }; 108 | 109 | class MenuWindows : public Windows 110 | { 111 | public: 112 | MenuWindows (DisplayPi* display); 113 | virtual ~MenuWindows (); 114 | 115 | virtual void AddMenuItem (const char* label, IAction* action = nullptr); 116 | virtual void AddCheckMenuItem (const char* label, bool* value, IAction* action = nullptr); 117 | virtual void RedrawWindow (); 118 | virtual IAction::ActionReturn HandleEvent( IEvent::Event event); 119 | virtual void SetFocus (unsigned int index = 0); 120 | 121 | protected: 122 | int current_focus_; 123 | std::vector list_item_; 124 | 125 | 126 | }; 127 | 128 | class CheckMenuItemWindows : public MenuItemWindows 129 | { 130 | public: 131 | CheckMenuItemWindows (DisplayPi* display); 132 | virtual ~CheckMenuItemWindows (); 133 | 134 | virtual void CreateWindow (const char* label, bool* value, Windows* parent, int x, int y, unsigned int width, unsigned int height); 135 | 136 | virtual void RedrawWindow (); 137 | virtual IAction::ActionReturn HandleEvent( IEvent::Event event); 138 | 139 | protected: 140 | bool * value_; 141 | }; 142 | -------------------------------------------------------------------------------- /src/emulation.cpp: -------------------------------------------------------------------------------- 1 | #include "emulation.h" 2 | #include "ScreenMenu.h" 3 | 4 | 5 | 6 | 7 | Emulation::Emulation(CMemorySystem* pMemorySystem, CLogger* log, CTimer* timer) 8 | : 9 | #ifdef ARM_ALLOW_MULTI_CORE 10 | CMultiCoreSupport(pMemorySystem), 11 | #endif 12 | logger_(log), 13 | timer_(timer), 14 | sound_mutex_(IRQ_LEVEL), 15 | setup_(nullptr), 16 | motherboard_(nullptr), 17 | display_(nullptr), 18 | keyboard_(nullptr), 19 | sound_(nullptr), 20 | sound_mixer_(nullptr), 21 | sound_is_ready(false), 22 | sound_run_(true) 23 | 24 | { 25 | setup_ = new SugarPiSetup(log); 26 | sound_mixer_ = new SoundMixer(); 27 | } 28 | 29 | Emulation::~Emulation(void) 30 | { 31 | delete motherboard_; 32 | } 33 | 34 | boolean Emulation::Initialize(DisplayPi* display, SoundPi* sound, KeyboardPi* keyboard, CScheduler *scheduler) 35 | { 36 | log_.SetLogger(logger_); 37 | logger_->Write("Kernel", LogNotice, "Emulation::Initialize"); 38 | 39 | sound_ = sound; 40 | display_ = display; 41 | keyboard_ = keyboard; 42 | scheduler_ = scheduler; 43 | 44 | sound_mixer_->Init(sound_, nullptr); 45 | 46 | logger_->Write("Kernel", LogNotice, "Creating Motherboard"); 47 | motherboard_ = new Motherboard(sound_mixer_, keyboard_); 48 | 49 | sound_mixer_->SetLog(&log_); 50 | motherboard_->SetLog(&log_); 51 | 52 | motherboard_->SetPlus(true); 53 | motherboard_->InitMotherbard(nullptr, nullptr, display_, nullptr, nullptr, nullptr); 54 | motherboard_->GetPSG()->SetLog(&log_); 55 | motherboard_->GetPSG()->InitSound(sound_); 56 | 57 | motherboard_->OnOff(); 58 | motherboard_->GetMem()->InitMemory(); 59 | motherboard_->GetMem()->SetRam(1); 60 | motherboard_->GetCRTC()->DefinirTypeCRTC(CRTC::AMS40226); 61 | motherboard_->GetVGA()->SetPAL(true); 62 | 63 | // Setup 64 | setup_->Init(display, sound_mixer_, motherboard_, keyboard_); 65 | setup_->Load(); 66 | 67 | motherboard_->GetPSG()->Reset(); 68 | motherboard_->GetSig()->Reset(); 69 | motherboard_->InitStartOptimizedPlus(); 70 | motherboard_->OnOff(); 71 | 72 | 73 | #ifdef ARM_ALLOW_MULTI_CORE 74 | logger_->Write("Kernel", LogNotice, "CMultiCoreSupport is going to initialize"); 75 | return CMultiCoreSupport::Initialize(); 76 | #else 77 | logger_->Write("Kernel", LogNotice, "End of Emulation init."); 78 | return TRUE; 79 | #endif 80 | } 81 | 82 | void Emulation::Run(unsigned nCore) 83 | { 84 | #ifdef ARM_ALLOW_MULTI_CORE 85 | switch (nCore) 86 | { 87 | case 0: 88 | // Run sound loop 89 | //sound_mutex_.Acquire(); 90 | sound_is_ready = true; 91 | //sound_mutex_.Release(); 92 | logger_->Write("Sound", LogNotice, "SoundMixer Started"); 93 | while(sound_run_) 94 | { 95 | sound_mixer_->PrepareBufferThread(); 96 | keyboard_->UpdatePlugnPlay(); 97 | scheduler_->Yield(); 98 | } 99 | 100 | logger_->Write("Sound", LogNotice, "SoundMixer Ended"); 101 | break; 102 | case 1: 103 | // Run Main loop 104 | { 105 | bool exit_loop = false; 106 | while (!exit_loop ) 107 | { 108 | sound_mutex_.Acquire(); 109 | exit_loop = sound_is_ready; 110 | sound_mutex_.Release(); 111 | 112 | // Checkin for sound is ready 113 | CTimer::Get ()->MsDelay (50); 114 | logger_->Write("CORE", LogNotice, "Waiting to start...."); 115 | } 116 | #endif 117 | logger_->Write("CORE", LogNotice, "Main loop"); 118 | RunMainLoop(); 119 | sound_run_ = false; 120 | CTimer::Get ()->MsDelay (1000); 121 | logger_->Write("CORE", LogNotice, "Exiting..."); 122 | #ifdef ARM_ALLOW_MULTI_CORE 123 | break; 124 | } 125 | case 2: 126 | // Display loop 127 | logger_->Write("CORE", LogNotice, "Display Loop started"); 128 | //display_->Loop(); 129 | logger_->Write("CORE", LogNotice, "Display Loop Ended"); 130 | 131 | default: 132 | break; 133 | } 134 | #endif 135 | } 136 | 137 | 138 | 139 | void Emulation::RunMainLoop() 140 | { 141 | ScreenMenu menu(&log_ ,logger_, display_, sound_mixer_, keyboard_, motherboard_, setup_); 142 | unsigned nCelsiusOldTmp = 0; 143 | int count = 0; 144 | bool finished = false; 145 | while (!finished ) 146 | { 147 | #define TIME_SLOT 10000 148 | motherboard_->StartOptimizedPlus(4 * TIME_SLOT*10); 149 | 150 | // Menu launched ? 151 | if (keyboard_->IsSelect()) 152 | { 153 | CCPUThrottle::Get()->SetSpeed(CPUSpeedLow); 154 | // todo : find a smart way to signal exit 155 | /*finished = */(menu.Handle()/* == IAction::Action_Shutdown*/); 156 | keyboard_->ReinitSelect(); 157 | CCPUThrottle::Get()->SetSpeed(CPUSpeedMaximum); 158 | } 159 | else 160 | { 161 | if (count == 10) 162 | { 163 | // Temperature 164 | unsigned nCelsius = CCPUThrottle::Get()->GetTemperature(); 165 | if (nCelsiusOldTmp != nCelsius) 166 | { 167 | logger_->Write("Kernel", LogNotice, "Temperature = %i", nCelsius); 168 | nCelsiusOldTmp = nCelsius; 169 | } 170 | 171 | 172 | count = 0; 173 | 174 | // Timing computation 175 | //static unsigned old = 0; 176 | /*unsigned elapsed = timer_->GetTicks(); 177 | 178 | logger_->Write("Kernel", LogNotice, "1s => %i ticks", (elapsed - old)); 179 | old = elapsed;*/ 180 | } 181 | else 182 | { 183 | count++; 184 | } 185 | 186 | } 187 | } 188 | } -------------------------------------------------------------------------------- /src/emulation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include "CPCCore/CPCCoreEmu/Motherboard.h" 16 | #include "CPCCore/CPCCoreEmu/SoundMixer.h" 17 | #include "CPCCore/CPCCoreEmu/ILog.h" 18 | 19 | #include "DisplayPi.h" 20 | #include "KeyboardPi.h" 21 | #include "SoundPi.h" 22 | #include "SugarPiSetup.h" 23 | #include "log.h" 24 | 25 | class Emulation 26 | #ifdef ARM_ALLOW_MULTI_CORE 27 | : public CMultiCoreSupport 28 | #endif 29 | { 30 | public: 31 | Emulation(CMemorySystem* pMemorySystem, CLogger* log, CTimer* timer); 32 | ~Emulation(void); 33 | 34 | boolean Initialize(DisplayPi* display, SoundPi* sound, KeyboardPi* keyboard,CScheduler *scheduler); 35 | void Run(unsigned nCore); 36 | void RunMainLoop(); 37 | 38 | 39 | protected: 40 | 41 | CLogger* logger_; 42 | CTimer* timer_; 43 | CSpinLock sound_mutex_; 44 | CScheduler* scheduler_; 45 | 46 | SugarPiSetup* setup_; 47 | Motherboard* motherboard_; 48 | DisplayPi* display_; 49 | KeyboardPi* keyboard_; 50 | SoundPi* sound_; 51 | SoundMixer* sound_mixer_; 52 | Log log_; 53 | 54 | bool sound_is_ready; 55 | bool sound_run_; 56 | }; -------------------------------------------------------------------------------- /src/kernel.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // kernel.cpp 3 | // 4 | // Circle - A C++ bare metal environment for Raspberry Pi 5 | // Copyright (C) 2014-2018 R. Stange 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | // 20 | #include "kernel.h" 21 | #include "ScreenMenu.h" 22 | //#include "Cartridge.h" 23 | 24 | #define DRIVE "SD:" 25 | //#define DRIVE "USB:" 26 | 27 | 28 | CKernel::CKernel(void) 29 | : 30 | #if AARCH != 64 // non-MMU mode currently does not work with AArch64 31 | m_Memory(TRUE), // set this to TRUE to enable MMU and to boost performance 32 | #endif 33 | m_Timer(&m_Interrupt), 34 | m_Logger(m_Options.GetLogLevel(), &m_Timer), 35 | cpu_throttle_(nullptr), 36 | m_EMMC(&m_Interrupt, &m_Timer, &m_ActLED), 37 | dwhci_device_(&m_Interrupt, &m_Timer, TRUE), 38 | vchiq_(&m_Memory, &m_Interrupt), 39 | display_(nullptr), 40 | keyboard_(nullptr), 41 | sound_(nullptr), 42 | emulation_(&m_Memory, &m_Logger, &m_Timer) 43 | 44 | { 45 | display_ = new DisplayPi(&m_Logger, &m_Timer); 46 | keyboard_ = new KeyboardPi(&m_Logger, &dwhci_device_, &m_DeviceNameService); 47 | cpu_throttle_ = new CCPUThrottle(); 48 | exception_handler_ = new CExceptionHandler; 49 | } 50 | 51 | CKernel::~CKernel (void) 52 | { 53 | delete exception_handler_; 54 | delete cpu_throttle_; 55 | delete keyboard_; 56 | delete display_; 57 | delete sound_; 58 | } 59 | 60 | boolean CKernel::Initialize (void) 61 | { 62 | 63 | boolean bOK = TRUE; 64 | 65 | if (bOK) 66 | { 67 | bOK = display_->Initialization(); 68 | } 69 | if (bOK) 70 | { 71 | bOK = m_Serial.Initialize(115200); 72 | 73 | } 74 | 75 | if (bOK) 76 | { 77 | CDevice* pTarget = &m_Serial; 78 | //CDevice* pTarget = m_DeviceNameService.GetDevice(m_Options.GetLogDevice(), FALSE); 79 | /*if (pTarget == 0) 80 | { 81 | pTarget = display_->GetScreenDevice(); 82 | }*/ 83 | bOK = m_Logger.Initialize(pTarget); 84 | m_Logger.Write("Kernel", LogNotice, "Initialisation done for log."); 85 | 86 | } 87 | 88 | if (bOK) 89 | { 90 | bOK = m_Interrupt.Initialize(); 91 | m_Logger.Write("Kernel", LogNotice, "Interrupt initialization done : %i", bOK); 92 | } 93 | 94 | if (bOK) 95 | { 96 | bOK = m_Timer.Initialize(); 97 | m_Logger.Write("Kernel", LogNotice, "Timer initialization done : %i", bOK); 98 | } 99 | 100 | if (bOK) 101 | { 102 | bOK = m_EMMC.Initialize(); 103 | m_Logger.Write("Kernel", LogNotice, "EMMC initialization done : %i", bOK); 104 | } 105 | 106 | #ifndef USE_QEMU_SUGARPI 107 | m_Logger.Write("Kernel", LogNotice, "Initialisation of VCHIQ....."); 108 | if (bOK) 109 | { 110 | bOK = vchiq_.Initialize(); 111 | } 112 | m_Logger.Write("Kernel", LogNotice, "Initialisationfoe Done : %i", bOK); 113 | 114 | if (f_mount(&m_FileSystem, DRIVE, 1) != FR_OK) 115 | { 116 | m_Logger.Write("Kernel", LogPanic, "Cannot mount drive: %s", DRIVE); 117 | } 118 | 119 | #ifdef USE_VCHIQ_SOUND 120 | sound_ = new SoundPi(&m_Logger, &vchiq_, &scheduler_); 121 | #else 122 | sound_ = new SoundPi(&m_Logger, &m_Interrupt, &scheduler_); 123 | #endif 124 | 125 | m_Logger.Write("Kernel", LogNotice, "Creating SoundPI"); 126 | sound_->Initialize(); 127 | m_Logger.Write("Kernel", LogNotice, "SoundPI Initialized !"); 128 | 129 | if (bOK) 130 | { 131 | bOK = keyboard_->Initialize(); 132 | } 133 | #endif 134 | if (bOK) 135 | { 136 | m_Logger.Write("Kernel", LogNotice, "Initialisation emulation....."); 137 | bOK = emulation_.Initialize(display_, sound_, keyboard_, &scheduler_); // must be initialized at last 138 | m_Logger.Write("Kernel", LogNotice, "Initialisation done done !"); 139 | } 140 | 141 | m_Logger.Write("Kernel", LogNotice, "EDID..."); 142 | display_->ListEDID(); 143 | m_Logger.Write("Kernel", LogNotice, "EDID Done !"); 144 | 145 | m_Logger.Write("Kernel", LogNotice, "Initialisation done. Waiting for CPUThrottle %i", bOK ? 1 : 0); 146 | 147 | CCPUThrottle::Get()->SetSpeed(CPUSpeedMaximum); 148 | 149 | 150 | m_Logger.Write("Kernel", LogNotice, "Initialisation done. Result = %i - CPU Speed max value : %i", bOK?1:0, CCPUThrottle::Get()->GetMaxClockRate()); 151 | return bOK; 152 | } 153 | 154 | TShutdownMode CKernel::Run (void) 155 | { 156 | 157 | m_Logger.Write("Kernel", LogNotice, "Entering running mode..."); 158 | 159 | //while (1) 160 | { 161 | emulation_.Run(0); 162 | //scheduler_.Yield(); 163 | } 164 | 165 | 166 | 167 | /*unsigned nCelsiusOldTmp = 0; 168 | while (1) 169 | { 170 | 171 | // 200ms 172 | motherboard_emulation_->StartOptimizedPlus(4000*50*20); 173 | 174 | // Temperature 175 | unsigned nCelsius = CCPUThrottle::Get()->GetTemperature(); 176 | if (nCelsiusOldTmp != nCelsius) 177 | { 178 | m_Logger.Write("Kernel", LogNotice, "Temperature = %i", nCelsius); 179 | nCelsiusOldTmp = nCelsius; 180 | } 181 | 182 | // Menu launched ? 183 | if (keyboard_->IsSelect()) 184 | { 185 | // do it ! 186 | CCPUThrottle::Get()->SetSpeed(CPUSpeedLow); 187 | 188 | ScreenMenu menu(&m_Logger, display_, keyboard_, motherboard_emulation_); 189 | menu.Handle(); 190 | 191 | keyboard_->ReinitSelect(); 192 | CCPUThrottle::Get()->SetSpeed(CPUSpeedMaximum); 193 | } 194 | else 195 | { 196 | // Timing computation 197 | static unsigned old = 0; 198 | unsigned elapsed = m_Timer.GetTicks(); 199 | 200 | m_Logger.Write("Kernel", LogNotice, "Time for 1s emulation : %i ticks -> %i ms", elapsed - old, (elapsed - old)); 201 | old = elapsed; 202 | 203 | } 204 | } 205 | */ 206 | CTimer::Get ()->MsDelay (2000); 207 | m_Logger.Write("Kernel", LogNotice, "Exiting : Halt"); 208 | return ShutdownHalt; 209 | } 210 | -------------------------------------------------------------------------------- /src/kernel.h: -------------------------------------------------------------------------------- 1 | // 2 | // kernel.h 3 | // 4 | // Circle - A C++ bare metal environment for Raspberry Pi 5 | // Copyright (C) 2014 R. Stange 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | // 20 | #ifndef _kernel_h 21 | #define _kernel_h 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include 44 | #include 45 | 46 | #include "DisplayPi.h" 47 | #include "KeyboardPi.h" 48 | #include "SoundPi.h" 49 | #include "emulation.h" 50 | 51 | 52 | enum TShutdownMode 53 | { 54 | ShutdownNone, 55 | ShutdownHalt, 56 | ShutdownReboot 57 | }; 58 | 59 | 60 | class CKernel 61 | { 62 | public: 63 | CKernel (void); 64 | ~CKernel (void); 65 | 66 | boolean Initialize (void); 67 | int LoadCprFromBuffer(unsigned char* buffer, int size); 68 | 69 | TShutdownMode Run (void); 70 | 71 | protected: 72 | //void GetFolderCart(); 73 | 74 | private: 75 | // do not change this order 76 | CMemorySystem m_Memory; 77 | CActLED m_ActLED; 78 | CKernelOptions m_Options; 79 | CDeviceNameService m_DeviceNameService; 80 | CSerialDevice m_Serial; 81 | CTimer m_Timer; 82 | CLogger m_Logger; 83 | CInterruptSystem m_Interrupt; 84 | CCPUThrottle *cpu_throttle_; 85 | CEMMCDevice m_EMMC; 86 | CUSBHCIDevice dwhci_device_; 87 | CExceptionHandler * exception_handler_; 88 | FATFS m_FileSystem; 89 | 90 | CScheduler scheduler_; 91 | CVCHIQDevice vchiq_; 92 | //SoundMixer *sound_mixer_; 93 | //Motherboard *motherboard_emulation_; 94 | DisplayPi *display_; 95 | KeyboardPi *keyboard_; 96 | SoundPi* sound_; 97 | 98 | Emulation emulation_; 99 | 100 | }; 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /src/log.cpp: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | 3 | Log::Log() :logger_(nullptr) 4 | { 5 | 6 | } 7 | Log::~Log() 8 | { 9 | 10 | } 11 | void Log::SetLogger(CLogger* logger) 12 | { 13 | logger_ = logger; 14 | } 15 | 16 | void Log::WriteLog(const char* log) 17 | { 18 | logger_->Write("LOG", LogNotice, log); 19 | } 20 | void Log::WriteLogByte(unsigned char number) 21 | { 22 | logger_->Write("LOG", LogNotice, "%2.2X", number); 23 | } 24 | void Log::WriteLogShort(unsigned short number) 25 | { 26 | logger_->Write("LOG", LogNotice, "%4.4X", number); 27 | } 28 | void Log::WriteLog(unsigned int number) 29 | { 30 | logger_->Write("LOG", LogNotice, "%i", number); 31 | } 32 | void Log::EndOfLine() 33 | { 34 | logger_->Write("LOG", LogNotice, "\n"); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "CPCCore/CPCCoreEmu/ILog.h" 6 | 7 | class Log : public ILog 8 | { 9 | public: 10 | Log(); 11 | virtual ~Log(); 12 | void SetLogger(CLogger* logger); 13 | virtual void WriteLog(const char* log); 14 | virtual void WriteLogByte(unsigned char number); 15 | virtual void WriteLogShort(unsigned short number); 16 | virtual void WriteLog(unsigned int number); 17 | virtual void EndOfLine(); 18 | 19 | protected: 20 | CLogger* logger_; 21 | }; 22 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // main.c 3 | // 4 | // Circle - A C++ bare metal environment for Raspberry Pi 5 | // Copyright (C) 2014 R. Stange 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | // 20 | #include "kernel.h" 21 | #include 22 | 23 | int main (void) 24 | { 25 | // cannot return here because some destructors used in CKernel are not implemented 26 | 27 | CKernel Kernel; 28 | if (!Kernel.Initialize ()) 29 | { 30 | halt (); 31 | return EXIT_HALT; 32 | } 33 | 34 | TShutdownMode ShutdownMode = Kernel.Run (); 35 | 36 | switch (ShutdownMode) 37 | { 38 | case ShutdownReboot: 39 | reboot (); 40 | return EXIT_REBOOT; 41 | 42 | case ShutdownHalt: 43 | default: 44 | halt (); 45 | return EXIT_HALT; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sugarpi-action/Dockerfile: -------------------------------------------------------------------------------- 1 | # Download base image ubuntu 20.04 2 | FROM ubuntu:20.04 3 | 4 | # LABEL about the custom image 5 | LABEL maintainer="thomas.guillemin@gmail.com" 6 | LABEL version="0.1" 7 | LABEL description="This is custom Docker Image for \ 8 | building SugarPi for Raspberry Pi." 9 | 10 | # Disable Prompt During Packages Installation 11 | ARG DEBIAN_FRONTEND=noninteractive 12 | 13 | 14 | # Update Ubuntu Software repository 15 | RUN apt-get update && apt-get -qq install wget && apt-get -qq install tar && apt-get -qq install xz-utils && apt-get -qq install git && apt-get -qq install make && apt-get -qq install build-essential 16 | RUN wget https://developer.arm.com/-/media/Files/downloads/gnu-a/9.2-2019.12/binrel/gcc-arm-9.2-2019.12-x86_64-aarch64-none-elf.tar.xz 17 | RUN tar -Jxvf gcc-arm-9.2-2019.12-x86_64-aarch64-none-elf.tar.xz 18 | ENV PATH /gcc-arm-9.2-2019.12-x86_64-aarch64-none-elf/bin:$PATH 19 | 20 | COPY entrypoint_docker.sh / 21 | #ensure entrypoint is executable (force flags update, because file flags may change if a commit is made under Windows) 22 | RUN chmod +x /entrypoint_docker.sh 23 | 24 | # Code file to execute when the docker container starts up ('entrypoint_docker.sh') 25 | ENTRYPOINT ["/entrypoint_docker.sh"] 26 | 27 | 28 | -------------------------------------------------------------------------------- /sugarpi-action/action.yml: -------------------------------------------------------------------------------- 1 | # action.yml 2 | name: 'SugarPi Build' 3 | description: 'Build SugarPi for Rapsberry Pi' 4 | 5 | runs: 6 | using: 'docker' 7 | image: 'Dockerfile' 8 | -------------------------------------------------------------------------------- /sugarpi-action/entrypoint_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -l 2 | 3 | chmod +x ./build_all.sh 4 | chmod +x ./build_circle.sh 5 | chmod +x ./build_pi3.sh 6 | chmod +x ./build_pi4.sh 7 | 8 | ./build_all.sh 9 | --------------------------------------------------------------------------------