Skip to content

Commit

Permalink
v1.0 wip
Browse files Browse the repository at this point in the history
fix: do not render AY on 16/48 models (motos)
fix: yet another fix for our .sav/z80 corruption (add step,add pins,no prefetch)
new: .Spectral/Spectral.ini config file
chg: made ui_notify() animated
chg: bump up beeper core
chg: .Spectral/ tree layout made human-readable.
chg: .Spectral/ contents are zipped now.
chg: bump up 3rd_deflate.h
fix: 2 asan buffer overflows
chg: move tape browser to bottom screen
new: zxdb browser: progress bar
chg: zxdb browser: async/threaded
chg: zxdb browser: filtered thumbnails
fix: prevents hang on 3rd_tfd popups by initializing COM sooner (win)
new: AY player

new: game browser v2: zxdb based
chg: game browser v2: can bookmark and flag games now
chg: game browser v2: efforts to display romanized eastern games in A-B-C tabs
chg: game browser v2: recolorize png/jpg thumbnails to use our custom zx palette
chg: use (smaller) bescii punctuaction glyphs
chg: made ui to display fonts in both mono and variable sizes
fix: infinite loop while deallocating ansi console

redo border renderer (border break)

simplify .sav impl (iff1/2/ir)
fix 3 out of mem accesses (via sanitize address)
zxdb bump up latest
zxdb add bugfix tapes
zxdb retrieve features (tags)
zxdb retrieve non-initial releases too
zxdb do not download szx/slt snapshots (unsupported)
zxdb prevent from downloading null urls
zxdb improve search filters
zxdb update window title automatically

new: zxdb cache
new: append games to the spectral binary (copy /b spectral.exe+game.ext game.exe ; thanks for the idea callmesnake!)
fix: improve zxdb to detect tape_X/side_X cases
new: display zxdb bonus tracks
new: zxdb overlay panning (mouse)
fix: zxdb prefer branded searches or those with higher scores
fix: zxdb \t chars on instructions
fix: zxdb convert instructions to utf8 (gonzzalezz)
new: zxdb download games on demand
chg: can search for zxdb identifiers as well
  • Loading branch information
r-lyeh committed Jul 6, 2024
1 parent cf611bc commit e895b91
Show file tree
Hide file tree
Showing 57 changed files with 7,855 additions and 2,390 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt → .github/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.13...${CMAKE_VERSION})
project(Spectral LANGUAGES C CXX)
add_executable(Spectral src/zx.c src/sys_window.cc)
add_executable(Spectral ../src/app.c ../src/sys_window.cc)
target_compile_options(Spectral PRIVATE /MT /GL /GF /arch:AVX2)
target_compile_definitions(Spectral PRIVATE main=WinMain $<$<CONFIG:Release>:FLAGS=FLAGS_REL>)
target_link_options(Spectral PRIVATE /SUBSYSTEM:WINDOWS)
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- name: Configure CMake
run: >-
cmake
-S "${{github.workspace}}"
-S "${{github.workspace}}/.github"
-B "${{github.workspace}}/build"
-DCMAKE_BUILD_TYPE=${{env.CMAKE_BUILD_TYPE}}
-DCMAKE_GENERATOR_PLATFORM=${{env.CMAKE_GENERATOR_PLATFORM}}
Expand Down
75 changes: 52 additions & 23 deletions MAKE.bat
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,22 @@ if [ "$(uname)" != "Darwin" ]; then
[ ! -f ".setup" ] && sudo apt-get -y update && sudo apt-get -y install gcc libx11-dev gcc libgl1-mesa-dev libasound2-dev mesa-common-dev && echo>.setup

# compile --------------------------------------------------------------------
gcc src/zx.c -I src -o ./Spectral.linux -O3 -DNDEBUG=3 -Wno-unused-result -Wno-unused-value -Wno-format -Wno-multichar -Wno-pointer-sign -Wno-string-plus-int -Wno-empty-body -lm -lX11 -lGL -lasound -lpthread $* || exit
gcc src/app.c -I src -o ./Spectral.linux -O3 -DNDEBUG=3 -Wno-unused-result -Wno-unused-value -Wno-format -Wno-multichar -Wno-pointer-sign -Wno-string-plus-int -Wno-empty-body -lm -lX11 -lGL -lasound -lpthread $* || exit
upx -9 Spectral.linux
src/res/zxdb/append.linux Spectral.linux src/res/zxdb/Spectral.db.gz
src/res/embed.linux Spectral.linux @SpectralEmBeDdEd
src/res/embed.linux Spectral.linux src/res/zxdb/Spectral.db.gz
src/res/embed.linux Spectral.linux @SpectralEmBeDdEd

fi

if [ "$(uname)" = "Darwin" ]; then

# compile --------------------------------------------------------------------
export SDKROOT=$(xcrun --show-sdk-path)
gcc -ObjC src/zx.c -I src -o ./Spectral.osx -O3 -DNDEBUG=3 -Wno-unused-result -Wno-unused-value -Wno-format -Wno-multichar -Wno-pointer-sign -Wno-string-plus-int -Wno-empty-body -framework cocoa -framework iokit -framework CoreFoundation -framework CoreAudio -framework AudioToolbox -framework OpenGL -lm $* || exit
src/res/zxdb/append.osx Spectral.osx src/res/zxdb/Spectral.db.gz
gcc -ObjC src/app.c -I src -o ./Spectral.osx -O3 -DNDEBUG=3 -Wno-unused-result -Wno-unused-value -Wno-format -Wno-multichar -Wno-pointer-sign -Wno-string-plus-int -Wno-empty-body -framework cocoa -framework iokit -framework CoreFoundation -framework CoreAudio -framework AudioToolbox -framework OpenGL -lm $* || exit
src/res/embed.osx Spectral.osx @SpectralEmBeDdEd
src/res/embed.osx Spectral.osx src/res/zxdb/Spectral.db.gz
src/res/embed.osx Spectral.osx @SpectralEmBeDdEd

# embed icon and make .app
test -d Spectral.app && rm -rf Spectral.app
Expand All @@ -54,15 +58,19 @@ exit

@echo off

for /f "tokens=1,* delims= " %%a in ("%*") do set ALL_BUT_FIRST=%%b
for /f "tokens=1,* delims= " %%a in ("%*") do set ALL_FROM_2ND=%%b

if "%1"=="" (
make rel
)

if "%1"=="-h" (
echo make [dev^|opt^|rel] [compiler-flags]
echo make [dbg^|dev^|opt^|rel] [compiler-flags]
exit /b
)

if "%1"=="test" (
call make opt -DPRINTER -DTESTS %ALL_BUT_FIRST% || goto error
call make opt -DPRINTER -DTESTS %ALL_FROM_2ND% || goto error
pause

rem Z80------------------------------------------
Expand Down Expand Up @@ -98,26 +106,54 @@ if "%1"=="test" (
exit /b
)

if "%1"=="tidy" (
del *.obj
del *.exe
del *.pdb
del *.ilk
del *.zip
del src\res\zxdb\*.db
del src\res\zxdb\*.exe
del src\res\zxdb\*.sqlite
exit /b
)

if "%1"=="dev" (
cl src\zx.c src\sys_window.cc -I src /FeSpectral.exe /Zi %ALL_BUT_FIRST% || goto error
copy /y src\res\zxdb\Spectral.db.gz Spectral.db 1>nul 2>nul
call make nil /Zi %ALL_FROM_2ND% || goto error
src\res\embed Spectral.exe @SpectralEmBeDdEd
src\res\embed Spectral.exe src\res\zxdb\Spectral.db.gz
src\res\embed Spectral.exe @SpectralEmBeDdEd

tasklist /fi "ImageName eq remedybg.exe" 2>NUL | find /I "exe">NUL || start remedybg -q -g Spectral.exe
tasklist /fi "ImageName eq remedybg.exe" 2>NUL | find /I "exe">NUL || (where /q remedybg.exe && start remedybg -q -g Spectral.exe)

exit /b
)

if "%1"=="dbg" (
call make dev /fsanitize=address %ALL_FROM_2ND% || goto error
exit /b
)

if "%1"=="opt" (
rem do not use /O1 or /O2 below. ayumi drums will be broken in AfterBurner.dsk otherwise
call make nil /Ox /MT /DNDEBUG /GL /GF /arch:AVX2 %ALL_BUT_FIRST% || goto error
copy /y src\res\zxdb\Spectral.db.gz Spectral.db 1>nul 2>nul
call make nil /Ox /MT /DNDEBUG /GL /GF /arch:AVX2 %ALL_FROM_2ND% || goto error
where /q upx.exe && upx Spectral.exe
src\res\embed Spectral.exe @SpectralEmBeDdEd
copy /y Spectral.exe SpectralNoZXDB.exe
src\res\embed Spectral.exe src\res\zxdb\Spectral.db.gz
src\res\embed Spectral.exe @SpectralEmBeDdEd
exit /b
)

if "%1"=="rel" (
call make opt -Dmain=WinMain -DNDEBUG=3 %ALL_BUT_FIRST% || goto error
where /q upx.exe && upx Spectral.exe
src\res\zxdb\append Spectral.exe src\res\zxdb\Spectral.db.gz && if exist "Spectral.db" del Spectral.db
call make opt -Dmain=WinMain -DNDEBUG=3 %ALL_FROM_2ND% || goto error

del *.ilk 1> nul 2> nul
del *.pdb 1> nul 2> nul
del *.obj 1>nul 2>nul
del *.ilk 1>nul 2>nul
del *.pdb 1>nul 2>nul

exit /b 1
)

Expand All @@ -131,18 +167,11 @@ where /q cl.exe || call "%ProgramFiles(x86)%/microsoft visual studio/2019/commun
where /q cl.exe || call "%ProgramFiles(x86)%/microsoft visual studio/2017/community/VC/Auxiliary/Build/vcvarsx86_amd64.bat" >nul 2>nul

@echo on
cl src\zx.c src\sys_window.cc -I src /FeSpectral.exe %ALL_BUT_FIRST% || goto error
copy /y src\res\zxdb\Spectral.db.gz Spectral.db 1>nul 2>nul
cl src\app.c src\sys_window.cc -I src /FeSpectral.exe %ALL_FROM_2ND% || goto error

@echo off
where /Q ResourceHacker.exe && ResourceHacker.exe -open Spectral.exe -save Spectral.exe -action addskip -res src\res\img\noto_1f47b.ico -mask ICONGROUP,MAINICON,0

del *.ilk 1> nul 2> nul
del *.pdb 1> nul 2> nul
del *.obj 1>nul 2>nul
del *.ilk 1>nul 2>nul
del *.pdb 1>nul 2>nul

exit /b 0

:error
Expand Down
30 changes: 18 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Spectral <img src="src/res/img/noto_1f47b.png" width="5%" height="5%" />
Sinclair ZX Spectrum emulator from the 80s.

![image](https://github.com/r-lyeh/spectral/assets/35402248/8ae5f8d4-0a7c-41ee-9112-2e86bacdb262)
![image](https://github.com/r-lyeh/Spectral/assets/35402248/2575bc40-d19d-43a3-81d3-6d638e9a94d1)
![image](https://github.com/r-lyeh/Spectral/assets/35402248/8d8ee594-fafd-4538-993f-9840bf9fc245)
![image](https://github.com/r-lyeh/Spectral/assets/35402248/c1257c88-56c0-4325-926a-b0cbf8b19ae5)
![image](https://github.com/r-lyeh/Spectral/assets/35402248/99bc9b7a-aa8e-421b-bd8b-8556a4d0dfcb)

## About
Spectral is an experimental emulator that I have been randomly assembling [since the pandemic days](https://twitter.com/r_rlyeh/status/1280964279903158273), inspired by my old fZX32 emulator. Accuracy and performance are long-term goals, but the primary focus is just having fun with this thing. Hardcore ZX users will find little value in this emulator right now, but I hope newbies may find its ease of use somehow appealing to try.
Expand All @@ -23,20 +24,21 @@ Code is highly experimental and prone to change in the future. I will keep alter
- [x] DSK/EDSK/TRD/SCL/FDI/MGT/IMG/HOBETA disks.
- [x] SCR/PNG screenshots. <!-- ulaplus screenshots. video recording -->
- [x] ZIP/RAR/GZ archives.
- [x] AY tunes player.
- [x] µ765/Betadisk interfaces.
- [x] Auto load games. Auto play/stop tape. TurboROM.
- [x] Game browser. <!-- @todo: rewrite this -->
- [x] Graphical tape browser.
- [x] 50/60Hz fps lock.
- [x] Run-a-head.
- [x] POK support. <!-- @todo: cheats finder -->
- [x] POK support. <!-- @todo: cheats finder; useful? -->
- [x] Gunstick, Lightgun. <!-- Cheetah Defender Lightgun, Magnum Light Phaser, Stack Light Rifle -->
- [x] External shaders support.
- [x] Internal savestates.
- [x] Graphical User Interface.
- [x] Portable: Windows, Linux, MacOS.
- [x] ZXDB integration.
- [ ] Gallery marquee. <!-- Flex. Tape cases. ZX catalog on demand. -->
- [x] Embedded ZXDB.
- [x] ZXDB Gallery. <!-- Tape cases. -->
- [ ] Extra accurate Z80 backend. <!-- @todo: contended mem, contended ports, memptr, snow, Q, floating bus (+2a/+3) -->
- [ ] Cycle accurate (border, multicolor, etc).
- [ ] RZX support. <!-- @todo: rzx loadsave http://ramsoft.bbk.org.omegahg.com/rzxform.html -->
Expand All @@ -46,6 +48,13 @@ Code is highly experimental and prone to change in the future. I will keep alter
- [ ] Optimized.
- [x] Unlicensed.

## Downloads
Download any binary release from the [bin/ folder](bin/).

Alternatively, you can build the emulator yourself:
- Windows users double click `MAKE.bat` file.
- Linux/MacOS users can run `sh MAKE.bat` instead.

## Usage
Spectral can be configured with a mouse.

Expand All @@ -64,21 +73,18 @@ Here are some keyboard shortcuts, though:
- ALT+ENTER: Fullscreen
- TAB+CURSORS: Joysticks

## Build
Windows users double click `MAKE.bat` file. OSX/Linux users can run `sh MAKE.bat`.

## Credits
Andre Weissflog, for their many single-header libraries! (Zlib licensed). Peter Sovietov and wermipls, for their accurate AY chip emulator (MIT licensed). Ulrich Doewich and Colin Pitrat, for their uPD765A floppy disk controller (GPL licensed). Marat Fayzullin for their WD1793/FDI controllers (Propietary). Potapov Vsevolod Viktorovich for their rusfaq website. Andrew Owen and Geoff Wearmouth for their custom ROMs. Simon Owen for their DSK technical websites. Santiago Romero, Philip Kendall, James McKay for their FOSS emulators. Damian Vila for their BESCII truetype font (CC-1.0). lalaoopybee, for their lovely tube shader. Günter Woigk, Juan Carlos González Amestoy and David Colmenero for their floppy sound recordings. The ZX Spectrum Discord folks. All the ZX community!
Andre Weissflog, for their many single-header libraries! (Zlib licensed). Peter Sovietov and wermipls, for their accurate AY chip emulator (MIT licensed). Ulrich Doewich and Colin Pitrat, for their uPD765A floppy disk controller (GPL licensed). Marat Fayzullin for their WD1793/FDI controllers (Proprietary). Sergey Bulba for their ay2sna tool (Public Domain). Potapov Vsevolod Viktorovich for their rusfaq website. Andrew Owen and Geoff Wearmouth for their custom ROMs. Simon Owen for their DSK technical websites. Santiago Romero, Philip Kendall, James McKay for their FOSS emulators. Damian Vila for their BESCII truetype font (CC-1.0). lalaoopybee, for their lovely tube shader. Günter Woigk, Juan Carlos González Amestoy and David Colmenero for their floppy sound recordings. The ZXDB devs. The ZX Spectrum Discord folks. All the ZX community!

## Unlicense
This software is released into the [public domain](https://unlicense.org/). Also dual-licensed as [0-BSD](https://opensource.org/licenses/0BSD) or [MIT (No Attribution)](https://github.com/aws/mit-0) for those countries where public domain is a concern (sigh). Any contribution to this repository is implicitly subjected to the same release conditions aforementioned.

## Links
- [Introduction to the ZX](https://en.wikipedia.org/wiki/ZX_Spectrum), ZX entry on Wikipedia.
- [Introduction to the ZX Spectrum](https://en.wikipedia.org/wiki/ZX_Spectrum), entry on Wikipedia.
- [SpecEmu](https://specemu.zxe.io/), my favourite ZX emulator on Windows.
- [SpectrumComputing](https://spectrumcomputing.co.uk/), [WorldOfSpectrum](https://worldofspectrum.net/), [ZXArt](https://zxart.ee/), [Virtual TRDOS](https://vtrd.in/) and [ZXInfo](https://zxinfo.dk/) are best online resources (imho).
- [SpectrumComputing](https://spectrumcomputing.co.uk/), [WorldOfSpectrum](https://worldofspectrum.net/), [ZXArt](https://zxart.ee/), [Virtual TRDOS](https://vtrd.in/) and [ZXInfo](https://zxinfo.dk/) are the best online resources (imho).
- [Crash](https://archive.org/details/crash-magazine), [YourSinclair](https://archive.org/details/your-sinclair-magazine), [SinclairUser](https://archive.org/details/sinclair-user-magazine) and [MicroHobby(ES)](https://archive.org/details/microhobby-magazine) are great old paper magazines.
- [ZX database](https://github.com/zxdb/ZXDB), [game maps](https://maps.speccy.cz/), [game cheats](https://www.the-tipshop.co.uk/) and [game longplays](https://www.youtube.com/@rzxarchive).
- [ZXDB](https://github.com/zxdb/ZXDB), [game maps](https://maps.speccy.cz/), [game cheats](https://www.the-tipshop.co.uk/) and [game longplays](https://www.youtube.com/@rzxarchive).
- [Daily ZX videos](https://www.youtube.com/results?search_query=zx+spectrum&sp=CAI%253D), on YouTube.

[![](https://github.com/r-lyeh/Spectral/actions/workflows/build.yml/badge.svg)](https://github.com/r-lyeh/Spectral/actions/workflows/build.yml)
Empty file removed games/.copy your games here
Empty file.
108 changes: 13 additions & 95 deletions src/3rd.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
//-----------------------------------------------------------------------------
// 3rd party libs

//#define KISSDB_IMPLEMENTATION
//#include "3rd_kissdb.h"

#define TIGR_C
#define TIGR_DO_NOT_PRESERVE_WINDOW_POSITION
//#define TIGR_DO_NOT_PRESERVE_WINDOW_POSITION // @fixme: make it centered
#define run run2
#define border border2
#include "3rd_tigr.h"
#include "3rd_tigrobjc.h"
#include "3rd_tigrmousecursor.h"
#include "3rd_tigrdragndrop.h"
#undef border
#undef run

#if 0
#define LUA_IMPL // lua544
Expand Down Expand Up @@ -34,13 +41,15 @@
#include "3rd_stbimage.h"
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "3rd_stbimage_resize2.h"
#define THREAD_IMPLEMENTATION
#include "3rd_thread.h"
#include "3rd_bin.h"

#if 1
#define TFD_IMPLEMENTATION
extern Tigr *app;
#define GetForegroundWindow() ((HWND)(app->handle))
//#define GetForegroundWindow GetActiveWindow
#include "3rd_tfd.h"
#undef GetForegroundWindow
//#undef GetForegroundWindow
#else
#include "3rd_osdialog.h"
#include "3rd_osdialog.c"
Expand All @@ -54,94 +63,3 @@ extern Tigr *app;
#include "3rd_osdialog_gtk3.c"
#endif
#endif


#include "3rd_bin.h"


#ifdef _WIN32

#include <wininet.h>
#pragma comment(lib,"wininet")

char* download( const char *url, int *len ) { // must free() after use
char *ptr = 0; int cap = 0;

int ok = 0;
char buffer[ 4096 ];
DWORD response_size = 0;

for( HINTERNET session = InternetOpenA("" /*"fwk.download_file"*/, PRE_CONFIG_INTERNET_ACCESS, NULL,NULL/*INTERNET_INVALID_PORT_NUMBER*/, 0); session; InternetCloseHandle(session), session = 0 ) // @fixme: download_file
for( HINTERNET request = InternetOpenUrlA(session, url, NULL, 0, INTERNET_FLAG_RELOAD|INTERNET_FLAG_SECURE, 0); request; InternetCloseHandle(request), request = 0 )
for( ; (ok = !!InternetReadFile(request, buffer, sizeof(buffer), &response_size)) && response_size > 0 ; ) {
ptr = realloc(ptr, cap += response_size );
memcpy(ptr + (cap - response_size), buffer, response_size);
}

if( !ok ) {
if( ptr ) free(ptr);
return NULL;
}

if( len ) *len = cap;
return ptr;
}

#elif 1

char* download( const char *url, int *len ) { // must free() after use
char *ptr = 0; int cap = 0;

int ok = 0;
char buffer[ 4096 ];

if( url[0] != '!' ) {
if( !ok ) sprintf(buffer, "!curl -L '%s' 2>/dev/null", url), ptr = download(buffer, len), ok = !!ptr;
if( !ok ) sprintf(buffer, "!wget -qO- '%s' 2>/dev/null", url), ptr = download(buffer, len), ok = !!ptr;
if( ok ) return ptr;
}
else
for( FILE *fp = popen(url+1, "r"); fp; pclose(fp), fp = 0)
for(; !feof(fp); ) {
int count = fread(buffer, 1, sizeof(buffer), fp);
ok = count > 0;
if(!ok) break;

ptr = realloc(ptr, cap += count );
memcpy(ptr + (cap - count), buffer, count);
}

if( !ok ) {
if( ptr ) free(ptr);
return NULL;
}

if( len ) *len = cap;
return ptr;
}

#else

#define HTTPS_IMPLEMENTATION
#define copy copy2
#include "3rd_https.h"
#undef I
#undef R

int download_file( FILE *out, const char *url ) {
int ok = false;
if( out ) for( https_t *h = https_get(url, NULL); h; https_release(h), h = NULL ) {
while (https_process(h) == HTTPS_STATUS_PENDING)
#ifdef _WIN32
Sleep(1); // 1ms
#else
usleep(1000); // 1ms
#endif
printf("fetch status L%d, %d %s\n\n%.*s\n", https_process(h), h->status_code, h->content_type, (int)h->response_size, (char*)h->response_data);
if(https_process(h) == HTTPS_STATUS_COMPLETED)
ok = fwrite(h->response_data, h->response_size, 1, out) == 1;
}
return ok;
}

#endif
8 changes: 8 additions & 0 deletions src/3rd_deflate.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
#ifndef DEFLATE_H
#define DEFLATE_H

// miniz.c v1.15 r4 - public domain de/inflate. See "unlicense" statement at http://unlicense.org/
// Rich Geldreich <richgel99@gmail.com>, last updated Oct. 13, 2013. Then stripped down by @r-lyeh.
// Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt

unsigned deflate_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags); // [0..(6)..9][10 (uber)]
unsigned deflate_decode(const void *in, unsigned inlen, void *out, unsigned outlen);
unsigned deflate_bounds(unsigned inlen, unsigned flags);
unsigned deflate_excess(unsigned flags);

#endif

#ifdef DEFLATE_C
#pragma once
Expand Down Expand Up @@ -1598,6 +1603,9 @@ unsigned deflate_encode(const void *in, unsigned inlen, void *out, unsigned outl
unsigned deflate_bounds(unsigned inlen, unsigned flags) {
return (unsigned)MZ_MAX(128 + (inlen * 110) / 100, 128 + inlen + ((inlen / (31 * 1024)) + 1) * 5);
}
unsigned deflate_excess(unsigned flags) {
return (unsigned)0;
}

#endif // DEFLATE_C

Expand Down
Loading

0 comments on commit e895b91

Please sign in to comment.