dmagnetic-0.32/0000755000175000017500000000000014076357622013007 5ustar dettusdettusdmagnetic-0.32/LICENSE.txt0000644000175000017500000000241414076357617014637 0ustar dettusdettusCopyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. dmagnetic-0.32/dMagnetic.60000644000175000017500000002643214076357617015004 0ustar dettusdettus.\" Process this file with .\" groff -man -Tascii dMagnetic.6 .\" .Dd July 19th, 2021 .Os OpenBSD .Dt dMagnetic 6 . .Sh NAME .Nm dMagnetic .Nd A Magnetic Scrolls Interpreter . . .Sh INTRODUCTION You wake up on a sunny August morning with birds singing, and the air fresh and clear. However, your joints are stiff and you have not woken up in your bedroom as you would have expected. Trying to recall what happened the night before, you manage to piece together a few brief glimpses to give the following account... .Sh SYNOPSIS . .Nm .Op Fl mag Ar MAGFILE.mag .Op Fl gfx Ar GFXFILE.gfx .br .Nm .Op Fl msdosdir Ar DIRECTORY/ .Op Fl vmode Ar high_ansi2 .br .Nm .Op Fl tworsc Ar DIRECTORY/TWO.RSC .Op Fl vmode Ar high_ansi .br .Nm .Op Fl d64 Ar DIRECTORY/FILE1.d64,DIRECTORY/FILE2.d64 .Op Fl vmode Ar sixel .br .Nm .Op Fl amstradcpc Ar DIRECTORY/FILE1.DSK,DIRECTORY/FILE2.DSK .Op Fl vmode Ar utf .br .Nm .Op Fl spectrum Ar DIRECTORY/DISKIMAGE.DSK .Op Fl vmode Ar none .br .Nm .Op Fl archimedes Ar DIRECTORY/DISKIMAGE.adf .br .Nm .Op Fl atarixl Ar DIRECTORY/FILE1.ATR,DIRECTORY/FILE2.ATR .br .Nm .Op Fl appleii Ar 1.NIB,2.2MG,3.WOZ .br .Nm .Op Fl ini Ar ini-file .Op GAME .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vrows Ar ROWS .Op Fl vcols Ar COLUMNS .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar none .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar monochrome .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar low_ansi .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar low_ansi2 .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar high_ansi .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar high_ansi2 .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar utf .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar sixel .Op Fl sres Ar 1024x768 .Op Fl sforce .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl valign Ar left .br .Nm .Op Fl vlog Ar LOGFILE .Op Fl vecho .Nm .Op Fl help .Op Fl helpini .Op Fl v .Op Fl bsd .Sh DESCRIPTION .Nm is an interpreter for classic text adventure games, or interactive fiction, such as "The Pawn" and "The Guild of Thieves". The following games are being supported: .Bl -tag -width 10 .It "The Pawn" .It "The Guild of Thieves" .It "Jinxter" .It "Fish!" .It "Myth" .It "Corruption" .It "Wonderland" .El . .Sh GAME BINARIES Currently, binaries in the .mag and .gfx format from the magnetic scrolls memorial at https://msmemorial.if-legends.org/memorial.php are being supported. Once downloaded, their position has to be made available to .Nm by editing dMagnetic.ini first. .br Other possibilities are loading the games from original releases, such as .Bl -tag -width 10 .It the MS-DOS binaries .It the Magnetic Windows Resource Files .It the Commodore C64 images in the d64 format .It Amstrad CPC and Spectrum128 images in the DSK format .It Acorn Archimedes images in the ADF/ADL (aka ADFS) format .It AtariXL/Atari800 images in the ATR format .It Apple II images in the 2MG, NIB or WOZ format .El . .Sh STARTING THE GAME Once the game(s) have been downloaded, the .mag and .gfx files can be provided via .Pp .Nm -mag pawn.mag -gfx pawn.gfx .Pp Alternatively, binaries from the MSDOS version of "The Pawn" or "The Guild of Thieves" can be used. The directory, in which they are, has to be provided via .Pp .Nm -msdosdir DIRECTORY/ -vmode high_ansi .Pp It is recommended to use the .Op Fl vmode Ar high_ansi2 option, since this renders the half-tone images slightly different. .Pp A third option is to use the resource files from "Wonderland" or the magnetic scrolls collection. For this, the location of the file ending with "TWO.RSC" has to be provided. .Pp .Nm -tworsc DIRECTORY/TWO.RSC -vmode high_ansi .br .Nm -tworsc DIRECTORY/GTWO.RSC -vmode high_ansi -ega .br The names of the other resource files is being calculated from that. With the optional parameter -ega, the EGA versions of the pictures are being shown. .Pp A fourth option is to use D64 image files from the Commodore 64 releases. They come in pairs, since both sides of the floppy disk were occupied. So the parameter requires a list of them, separated by komma: .Pp .Nm -d64 DIRECTORY/FILE1.d64,DIRECTORY/FILE2.d64 -vmode sixel .Pp A fifth option is to use DSK image files from the Amstrad CPC releases. They come in pairs, since noth sides of the floppy disk were occupied. So the parameter requires a list of them, separated by komma: .Pp .Nm -amstradcpc DIRECTORY/FILE1.DSK,DIRECTORY/FILE2.DSK -vmode sixel .Pp A sixth option is to use DSK image files from the Spectrum releases. Since this platform did not have the pictures, a single floppy disk was enough: .Pp .Nm -spectrum DIRECTORY/DISKIMAGE.DSK -vmode none .Pp A seventh option is to use ADF/ADL/ADFS image files from the Acorn Archimedes releases. .Pp .Nm -archimedes DIRECTORY/DISKIMAGE.ADF .Pp An eigth option is to use ATR image files from the AtariXL/Atari800 releases. Those come in pairs. .Pp .Nm -atarixl DIRECTORY/FILE1.ATR,DIRECTORY/FILE2.ATR .Pp An ninth option is to use image files from the Apple II releases. .Nm can handle NIB, 2MG and WOZ. For this platform, up to three disks were used, so three floppy images are required. .br .Pp .Nm -appleii 1.NIB,2.NIB,3.NIB .br .Nm -appleii 1.2MG,2.2MG,3.2MG .br .Nm -appleii 1.WOZ,2.WOZ,3.WOZ .br .Pp It is also possible to edit the dMagnetic.ini file and start a specific game, by using one of .Pp .Nm -ini dMagnetic.ini pawn .br .Nm -ini dMagnetic.ini guild .br .Nm -ini dMagnetic.ini jinxter .br .Nm -ini dMagnetic.ini corruption .br .Nm -ini dMagnetic.ini fish .br .Nm -ini dMagnetic.ini myth .br .Nm -ini dMagnetic.ini wonderland .Pp When you see the prompt, the game is ready to accept your commands. For example EXAMINE CLOTHES. Or GO EAST. Or ASK KRONOS ABOUT THE WRISTBAND. .br .Sh LEAVING THE GAME Type in "QUIT". Duh! .Sh SAVING/LOADING PROGRESS The game can be saved at any type by typing SAVE, and loaded by typing LOAD. Followed by a filename. .Pp SAVE myprogress1.sav .br LOAD myprogress1.sav .Sh TEXT ALIGNMENT Changing the alignment of the output text can be done with the .ini file or the commandline. .br .Nm Fl valign Ar left .br .Nm Fl valign Ar block .br .Nm Fl valign Ar right .br .Sh GRAPHIC MODES .Nm has been developed with ANSI-consoles in mind. To select a video output that best suits your needs, please try one of the following . .Pp .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar none .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar monochrome .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar low_ansi .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar low_ansi2 .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar high_ansi .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar high_ansi2 .br .Pp .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar utf .Pp .br To change the number of rows/columns to render the images, use .Pp .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vcols Ar COLUMNS .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vrows Ar ROWS .Pp Sixels are a signalling mode for some terminal emulators (like mlterm, or xterm -ti vt340 , for example), that offer the possibility of drawing high resolution images. It can be selected with .Pp .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar sixel .Op Fl sres Ar 1024x768 .Op Fl sforce .Pp where the -sres option offers a way to scale the images. The maximum allowed width is 4096 pixels. Instead of automatically using a matching aspect ratio, the -sforce option can be used to force the resolution. .Sh GRAPHICS IN WONDERLAND AND THE MAGNETIC SCROLLS COLLECTION Before you can see the beautiful graphics in "Wonderland", or from the Magnetic Scrolls Collection, you have to type in "graphics" to enable them. .Sh COMMAND LINE OPTIONS .Op Fl bsd .br Shows the license. .Pp .Op Fl Fl help .br Shows the detailed help. .Pp .Op Fl Fl helpini .br Shows an example for a working dMagnetic.ini file. .Pp .Op Fl mag Ar MAGFILE.mag .Op Fl gfx Ar GFXFILE.gfx .br .Op Fl msdosdir Ar DIRECTORY/ .br .Op Fl tworsc Ar DIRECTORY/TWO.RSC .br .Op Fl d64 Ar DIRECTORY/FILE1.d64,DIRECTORY/FILE2.d64 .br .Op Fl amstradcpc Ar DIRECTORY/FILE1.DSK,DIRECTORY/FILE2.DSK .br .Op Fl spectrum Ar DIRECTORY/DISKIMAGES.DSK .br .Op Fl archimedes Ar DIRECTORY/DISKIMAGES.ADF .br .Op Fl atarixl Ar DIRECTORY/FILE1.ATR,DIRECTORY/FILE2.ATR .br .Op Fl appleii Ar 1.NIB,2.2MG,3.WOZ .br .Nm is a Magnetic Scrolls Interpreter. To actually play the games, their binaries have to be provided. Either in the .mag and .gfx format from https://msmemorial.if-legends.org/magnetic.php, as the name of the directory in which the original MS-DOS version can be found, the location and the name of the second resource file TWO.RSC, as D64-images from the Commodore 64 releases, as DSK-images from the Amstrad/Schneider CPC releases, as DSK-images from the Spectrum releases, as ADF/ADL/ADFS images from the Acorn Archimedes releases, as ATR images from the AtariXL/Atari800 releases, or even as NIB/2MG/WOZ images from the Apple II releases. .Pp .Op Fl ini Ar dMagnetic.ini pawn .br .Op Fl ini Ar dMagnetic.ini guild .br .Op Fl ini Ar dMagnetic.ini jinxter .br .Op Fl ini Ar dMagnetic.ini corruption .br .Op Fl ini Ar dMagnetic.ini fish .br .Op Fl ini Ar dMagnetic.ini pawn .br .Op Fl ini Ar dMagnetic.ini myth .br .Op Fl ini Ar dMagnetic.ini wonderland .br An alternative way to provide the location of the .mag and .gfx, the image files or the directory name, is through a dMagnetic.ini file. See helpini or .Xr dMagneticini 5 for an example of a working dMagnetic.ini file. .br IT SHOULD BE NOTED that the default location for dMagnetic.ini is in the user's home directory. If the file is located there, "The Pawn", for example, can be started by typing .br .Nm pawn .br .Pp .Op Fl rmode Ar pseudo .Op Fl rseed Ar SEED .br .Op Fl rmode Ar real .br Certain elements of the game rely on chance. For this, the virtual machine within .Nm offers two possibilities: Playing against a "pseudo" random generator, which results in a certain degree of determinism. On the other hand, playing with "real" random values from the operating system, allows for a completely new experience. .Pp .Op Fl vrows Ar ROWS .br .Op Fl vcols Ar COLUMNS .br .Nm has been designed with terminal windows as main output in mind. The terminal window has a fixed number of rows and columns to print out text. Its size is limited, but it is used by .Nm to render the beautiful BEAUTIFUL pictures as well. To restrict the space in which they are rendered (in glorious ANSI art), those command set the upper limits. .br .Pp .Op Fl vecho .br When trying to run .Nm and redirecting the output into a file, the inputs are missing. This option reprints what was typed in, to allow for a spoilery script. .Pp .Op Fl vlog Ar LOGFILE.log .br Sort of a travel journal, this option lets you write the commands that where typed into a file. .br .Pp .Op Fl vmode Ar none .br .Op Fl vmode Ar monochrome .br .Op Fl vmode Ar low_ansi .br .Op Fl vmode Ar low_ansi2 .br .Op Fl vmode Ar high_ansi .br .Op Fl vmode Ar high_ansi2 .br .Op Fl vmode Ar utf .br .Op Fl vmode Ar sixel .Op Fl sres Ar 1024x768 .Op Fl sforce .br This option allows for selecting a different mode to render the images, should the actual one prove to be unsuitable for the preferred terminal program. .br .Pp .Op Fl version .br Shows the current version of .Nm . .Sh BUGS Report bugs to .An Aq dettus@dettus.net . Make sure to include DMAGNETIC somewhere in the subject. .Sh AUTHOR Written by .An Thomas Dettbarn .Sh SEE ALSO .Xr dMagneticini 5 dmagnetic-0.32/testcode/0000755000175000017500000000000014076357617014625 5ustar dettusdettusdmagnetic-0.32/testcode/minitest.mag0000644000175000017500000000062014076357617017145 0ustar dettusdettusMaSc*f<<r<o<o<m< <X<<<<V<i<r<t<u<a<l< <m<a<c<h<i<n<e< <i<s< <r<u<n<n<i<n<g<.< <P<l<e<a<s<e< <p<r<e<s<s< <E<n<t<e<r<.< <>< dmagnetic-0.32/testcode/magtest.c0000644000175000017500000001076414076357617016445 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "vm68k.h" #include "linea.h" #include "default_callbacks.h" #define MAXMAGSIZE (1<<20) #define MAXGFXSIZE (1<<22) int main(int argc,char** argv) { FILE *f; int sizevm68k; int sizelineA; int sizeGUI; int retval=0; unsigned char* magloader; int magsize; unsigned char* gfxloader; int gfxsize; void *hVM68k; void *hLineA; void *hGUI; unsigned char* sharedMem; int unknownopcode; int sharedmemsize; fprintf(stderr,"*** dMagnetic- magtest\n"); fprintf(stderr,"*** Use at your own risk\n"); fprintf(stderr,"*** (C)opyright 2021 by dettus@dettus.net\n"); fprintf(stderr,"*****************************************\n"); fprintf(stderr,"\n"); if (argc!=3) { fprintf(stderr,"Please run with %s MAGFILE.mag GFXFILE.gfx\n",argv[0]); fprintf(stderr," where MAGFILE.mag/GFXFILE.gfx is one of\n"); fprintf(stderr," pawn.mag pawn.gfx\n"); exit(0); } magloader=malloc((MAXMAGSIZE));fprintf(stderr,"allocating %7d bytes for loading mag files\n",(MAXMAGSIZE)); f=fopen(argv[1],"rb"); if (f==NULL) { fprintf(stderr,"error opening file %s\n",argv[1]); exit(0); } magsize=fread(magloader,sizeof(char),(MAXMAGSIZE),f); fclose(f); gfxloader=malloc((MAXGFXSIZE));fprintf(stderr,"allocating %7d bytes for loading gfx files\n",(MAXGFXSIZE)); f=fopen(argv[2],"rb"); if (f==NULL) { fprintf(stderr,"error opening file %s\n",argv[2]); exit(0); } gfxsize=fread(gfxloader,sizeof(char),(MAXGFXSIZE),f); fclose(f); retval=vm68k_getsize(&sizevm68k); retval=lineA_getsize(&sizelineA); retval=default_getsize(&sizeGUI); if (retval) {fprintf(stderr,"getsize returned %d\n",retval);exit(0);} hVM68k=malloc(sizevm68k);fprintf(stderr,"allocating %7d bytes for 68000 core\n",sizevm68k); hLineA=malloc(sizelineA);fprintf(stderr,"allocating %7d bytes for lineA core\n",sizelineA); hGUI=malloc(sizeGUI);fprintf(stderr,"allocating %7d bytes for the GUI\n",sizeGUI); sharedMem=malloc(65536);fprintf(stderr,"allocating %7d bytes for shared mem\n",65536); sharedmemsize=65536; retval=vm68k_init(hVM68k,sharedMem,65536,0); retval=lineA_init(hLineA,sharedMem,&sharedmemsize,magloader,magsize,gfxloader,gfxsize); retval=default_open(hGUI,NULL,argc,argv); retval=lineA_setCBoutputChar(hLineA,default_cbOutputChar, hGUI); retval=lineA_setCBoutputString(hLineA,default_cbOutputString, hGUI); retval=lineA_setCBinputString(hLineA,default_cbInputString, hGUI); retval=lineA_setCBDrawPicture(hLineA,default_cbDrawPicture, hGUI); retval=lineA_setCBLoadGame(hLineA,default_cbLoadGame, hGUI); retval=lineA_setCBSaveGame(hLineA,default_cbSaveGame, hGUI); // the mag and gfx buffers are no longer necessary free(gfxloader); free(magloader); do { unsigned short opcode; unknownopcode=0; retval=vm68k_getNextOpcode(hVM68k,&opcode); if (retval==VM68K_OK) retval=lineA_substitute_aliases(hLineA,&opcode); if (retval==LINEA_OK) retval=vm68k_singlestep(hVM68k,opcode); if (retval!=VM68K_OK) retval=lineA_singlestep(hLineA,hVM68k,opcode); if (retval!=LINEA_OK && retval!=LINEA_OK_QUIT) { printf("\x1b[0m\n\x1b[0;37;44m unknown opcode %04X\x1b[0m\n",opcode); unknownopcode=1; } if (retval==LINEA_OK_QUIT) printf("\x1b[0m\n\x1b[0;37;44mGoodbye!\x1b[0m\n"); } while (!unknownopcode && !retval); } dmagnetic-0.32/testcode/instmatcher.c0000644000175000017500000000060314076357617017311 0ustar dettusdettus#include #include #include "vm68k_datatypes.h" #include "vm68k_decode.h" int main(int argc,char** argv) { int i; tVM68k_instruction inst; char name[16]; for (i=0;i<0xffff;i++) { vm68k_get_instructionname(vm68k_decode(i),name); printf("%04X: %s\n",i,name); } i=0x0880; vm68k_get_instructionname(vm68k_decode(i),name); printf("%04X: %s\n",i,name); } dmagnetic-0.32/testcode/README.minitest0000644000175000017500000000053514076357617017343 0ustar dettusdettusTo start the minitest, run % dMagnetic -mag minitest.mag -gfx minitest.gfx It should print a headline, saying "ROOM X". Then there should be a picture, depicting an X on a blue background. Finally, there should be a text, saying Virtual machine is running. Please press Enter. followed by the prompt >. Pressing Enter should close dMagnetic. dmagnetic-0.32/testcode/mkdotest.sh0000644000175000017500000000125614076357617017017 0ustar dettusdettus#!/bin/sh export SHA256_CMD=sha256sum export ECHO_CMD=echo export AWK_CMD=awk echo "export SHA256_CMD="$SHA256_CMD echo "export ECHO_CMD="$ECHO_CMD echo "export AWK_CMD="$AWK_CMD echo "\tif [ true \\" for I in none monochrome monochrome_inv low_ansi low_ansi2 high_ansi high_ansi2 sixel do ( echo -n "\${ECHO_CMD} Hello | ./dMagnetic -ini dMagnetic.ini -vmode "$I" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | \${SHA256_CMD} | \${AWK_CMD} -F' ' '{ print \$1; }' - " ) >tmp1.sh export TEST_OUT=`sh tmp1.sh` echo "\t\t-a \"\``cat tmp1.sh`\`\" = \""$TEST_OUT"\" \\" done echo "\t] ; then echo OK ; else echo \"expected output not seen\"; exit 1; fi" dmagnetic-0.32/testcode/minitest.gfx0000644000175000017500000000311014076357617017162 0ustar dettusdettusMaPiHd@ DD""r"''wwWF肆  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVs jDl!0F˜#bCd`͚6|EBY"QrEB"QrEB"QrEB"QrEB"QrEB"QrEB"EE4 i ѼTZ*$)$TZ*.SBQhQhMEBEE3 jI dmagnetic-0.32/Makefile0000644000175000017500000000715414076357617014462 0ustar dettusdettus#!/usr/bin/make -f # #Copyright 2021, dettus@dettus.net # #Redistribution and use in source and binary forms, with or without modification, #are permitted provided that the following conditions are met: # #1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # #2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # #THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED #WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE #DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE #FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL #DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR #SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER #CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, #OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PREFIX?=/usr/local INSTALLBIN=$(PREFIX)/bin INSTALLSHARE=$(PREFIX)/share/games INSTALLMAN=$(PREFIX)/share/man CC?=gcc AR?=ar #CFLAGS=-g -O0 CFLAGS?=-O3 CFLAGS+=-Wall #CFLAGS+=-Werror PROJ_HOME=./ INCFLAGS=-I$(PROJ_HOME)src/gui -I$(PROJ_HOME)src/toplevel -I$(PROJ_HOME)src/loader -I$(PROJ_HOME)src/engine/vm68k -I$(PROJ_HOME)src/engine/linea -I$(PROJ_HOME)src/engine/include OBJDIR=$(PROJ_HOME)obj/ LINK=$(CC) LDFLAGS+="-L"$(OBJDIR) SOURCES_LOADER= \ src/loader/loader_common.c \ src/loader/loader_msdos.c \ src/loader/loader_mw.c \ src/loader/loader_d64.c \ src/loader/loader_dsk.c \ src/loader/loader_archimedes.c \ src/loader/loader_atarixl.c \ src/loader/loader_appleii.c \ src/loader/maggfxloader.c SOURCES_LINEA= \ src/engine/linea/gfx1loader.c \ src/engine/linea/gfx2loader.c \ src/engine/linea/linea.c SOURCES_VM68K= \ src/engine/vm68k/vm68k.c \ src/engine/vm68k/vm68k_decode.c \ src/engine/vm68k/vm68k_loadstore.c SOURCES_GUI= \ src/gui/default_callbacks.c \ src/gui/default_palette.c \ src/gui/default_render.c SOURCES_TOPLEVEL= \ src/toplevel/configuration.c \ src/toplevel/dMagnetic_helpscreens.c \ src/toplevel/dMagnetic.c OBJ_LOADER=${SOURCES_LOADER:.c=.o} OBJ_LINEA=${SOURCES_LINEA:.c=.o} OBJ_VM68K=${SOURCES_VM68K:.c=.o} OBJ_GUI=${SOURCES_GUI:.c=.o} OBJ_TOPLEVEL=${SOURCES_TOPLEVEL:.c=.o} all: dMagnetic dMagnetic.ini # strip dMagnetic clean: rm -rf dMagnetic dMagnetic.ini rm -rf $(OBJ_LOADER) rm -rf $(OBJ_LINEA) rm -rf $(OBJ_VM68K) rm -rf $(OBJ_GUI) rm -rf $(OBJ_TOPLEVEL) install: all dMagnetic.6 dMagneticini.5 mkdir -p $(INSTALLBIN) mkdir -p $(INSTALLMAN)/man6/ mkdir -p $(INSTALLMAN)/man5/ mkdir -p $(INSTALLSHARE)/dMagnetic/ cp dMagnetic $(INSTALLBIN) cp dMagnetic.6 $(INSTALLMAN)/man6/ cp dMagneticini.5 $(INSTALLMAN)/man5/ cp README.txt $(INSTALLSHARE)/dMagnetic/ cp LICENSE.txt $(INSTALLSHARE)/dMagnetic/ cp dMagnetic.ini $(INSTALLSHARE)/dMagnetic/ dMagnetic: $(OBJ_LOADER) $(OBJ_LINEA) $(OBJ_VM68K) $(OBJ_GUI) $(OBJ_TOPLEVEL) $(LINK) $(LDFLAGS) -o $@ $(OBJ_LOADER) $(OBJ_LINEA) $(OBJ_VM68K) $(OBJ_GUI) $(OBJ_TOPLEVEL) dMagnetic.ini: dMagnetic ./dMagnetic -helpini >dMagnetic.ini .c.o: $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_EXTRA) $(INCFLAGS) -c -o $@ $< ## in case some post-compilation checks are needed ## if not, please comment out this line include ./checks.mk dmagnetic-0.32/run.sh0000644000175000017500000000006514076357617014154 0ustar dettusdettus#!/bin/sh ./dMagnetic -ini dMagnetic.ini wonderland dmagnetic-0.32/checks.mk0000644000175000017500000001647414076357617014620 0ustar dettusdettus#Copyright 2021, dettus@dettus.net # #Redistribution and use in source and binary forms, with or without modification, #are permitted provided that the following conditions are met: # #1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # #2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # #THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED #WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE #DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE #FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL #DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR #SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER #CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, #OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ### the following lines are the post-compilation test. This is a formality on some operating systems ########################### ECHO_CMD?=echo SHA256_CMD?=sha256 AWK_CMD?=awk ## the checks work by checking the sha256 sum of the output. ## since this one relies on the input, as well as the .ini file, ## it might not work as a post-install check. it is more of a ## post-compilation check, to see if it would support a new platform. ## check those graphic modes CHECKS= \ check-none \ check-monochrome \ check-monochrome_inv \ check-low_ansi \ check-low_ansi2 \ check-high_ansi \ check-high_ansi2 \ check-sixel \ check-utf ### this is the input. it does not really matter, but I thought I put in some easter egg in here. ### :::W WEYHE IST BUNT!! INPUT_none= "Weyhe-Leeste" INPUT_monochrome= "Sudweyhe" INPUT_monochrome_inv= "Melchiorshausen" INPUT_low_ansi= "Ahausen" INPUT_low_ansi2= "Kirchweyhe" INPUT_high_ansi= "Jeebel" INPUT_high_ansi2= "Erichshof" INPUT_sixel= "Dreye" INPUT_utf= "Lahausen" ### this is the output CHECKSUM_none= "3211640dc669f6b960a84a51559fc88a25bbc26966f01cdf44b9f4d9f4d71e1c" CHECKSUM_monochrome= "aa0614428ccdb7e7806a4f829f9ababa8c109e7487437fab1bf449fb2534c98b" CHECKSUM_monochrome_inv="56946544673dfbfa0d3769bbbab82bf942e8dcfc2dabf463cc734ac0133ea53f" CHECKSUM_low_ansi= "ab7f5805f56e453a75d93910321e27d99d6ebd23c36c5af12f4017bbffa650ef" CHECKSUM_low_ansi2= "c7b8c38ecb041fc71e0e29dbd93bb8687d15205893c0edf90c02ae8fc4ddf45d" CHECKSUM_high_ansi= "bcc24e07c6e207bd4988e11d7e3cbaa0594820670bf1f3df530722700934e970" CHECKSUM_high_ansi2= "e83e480afc67de933cc51dacf7a68d3c5c3fa4221fb299edf4bda495caf68bdc" CHECKSUM_sixel= "f6dafb92b7da82cedb14f1620064dc1da93b6f36ddf8b7989c95831a625b0a00" CHECKSUM_utf= "f484470b10a415b6a6aea37778055c7a2649e285078a32f33ef558de0228de85" ## so, here is my problem: I wanted to be able to run those checks in GNU make as well as BSD make. ## both of them have great features, like check-%, for-loops and check-{none,monochrome,sixel} as ## targets. but none of those features worked in both makes. ## the only way it truely did what it was supposed to do was by having a little bit of spaghettified ## code. please enjoy. ## ## btw: if you want to run the checks on Linux, run ## make SHA256_CMD="sha256sum" check # the code for the 8 checks is IDENTICAL. the only difference is the target's name. check-none: dMagnetic dMagnetic.ini if [ "`${ECHO_CMD} ${INPUT_${@:check-%=%}} | ./dMagnetic -ini dMagnetic.ini -vmode "${@:check-%=%}" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | ${SHA256_CMD} | ${AWK_CMD} -F' ' '{ print $$1; }' - `" = ${CHECKSUM_${@:check-%=%}} ] ; then ${ECHO_CMD} "$@ OK" ; else ${ECHO_CMD} "$@ failed" ; exit 1 ; fi check-monochrome: dMagnetic dMagnetic.ini if [ "`${ECHO_CMD} ${INPUT_${@:check-%=%}} | ./dMagnetic -ini dMagnetic.ini -vmode "${@:check-%=%}" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | ${SHA256_CMD} | ${AWK_CMD} -F' ' '{ print $$1; }' - `" = ${CHECKSUM_${@:check-%=%}} ] ; then ${ECHO_CMD} "$@ OK" ; else ${ECHO_CMD} "$@ failed" ; exit 1 ; fi check-monochrome_inv: dMagnetic dMagnetic.ini if [ "`${ECHO_CMD} ${INPUT_${@:check-%=%}} | ./dMagnetic -ini dMagnetic.ini -vmode "${@:check-%=%}" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | ${SHA256_CMD} | ${AWK_CMD} -F' ' '{ print $$1; }' - `" = ${CHECKSUM_${@:check-%=%}} ] ; then ${ECHO_CMD} "$@ OK" ; else ${ECHO_CMD} "$@ failed" ; exit 1 ; fi check-low_ansi: dMagnetic dMagnetic.ini if [ "`${ECHO_CMD} ${INPUT_${@:check-%=%}} | ./dMagnetic -ini dMagnetic.ini -vmode "${@:check-%=%}" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | ${SHA256_CMD} | ${AWK_CMD} -F' ' '{ print $$1; }' - `" = ${CHECKSUM_${@:check-%=%}} ] ; then ${ECHO_CMD} "$@ OK" ; else ${ECHO_CMD} "$@ failed" ; exit 1 ; fi check-low_ansi2: dMagnetic dMagnetic.ini if [ "`${ECHO_CMD} ${INPUT_${@:check-%=%}} | ./dMagnetic -ini dMagnetic.ini -vmode "${@:check-%=%}" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | ${SHA256_CMD} | ${AWK_CMD} -F' ' '{ print $$1; }' - `" = ${CHECKSUM_${@:check-%=%}} ] ; then ${ECHO_CMD} "$@ OK" ; else ${ECHO_CMD} "$@ failed" ; exit 1 ; fi check-high_ansi: dMagnetic dMagnetic.ini if [ "`${ECHO_CMD} ${INPUT_${@:check-%=%}} | ./dMagnetic -ini dMagnetic.ini -vmode "${@:check-%=%}" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | ${SHA256_CMD} | ${AWK_CMD} -F' ' '{ print $$1; }' - `" = ${CHECKSUM_${@:check-%=%}} ] ; then ${ECHO_CMD} "$@ OK" ; else ${ECHO_CMD} "$@ failed" ; exit 1 ; fi check-high_ansi2: dMagnetic dMagnetic.ini if [ "`${ECHO_CMD} ${INPUT_${@:check-%=%}} | ./dMagnetic -ini dMagnetic.ini -vmode "${@:check-%=%}" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | ${SHA256_CMD} | ${AWK_CMD} -F' ' '{ print $$1; }' - `" = ${CHECKSUM_${@:check-%=%}} ] ; then ${ECHO_CMD} "$@ OK" ; else ${ECHO_CMD} "$@ failed" ; exit 1 ; fi check-sixel: dMagnetic dMagnetic.ini if [ "`${ECHO_CMD} ${INPUT_${@:check-%=%}} | ./dMagnetic -ini dMagnetic.ini -vmode "${@:check-%=%}" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | ${SHA256_CMD} | ${AWK_CMD} -F' ' '{ print $$1; }' - `" = ${CHECKSUM_${@:check-%=%}} ] ; then ${ECHO_CMD} "$@ OK" ; else ${ECHO_CMD} "$@ failed" ; exit 1 ; fi check-utf: dMagnetic dMagnetic.ini if [ "`${ECHO_CMD} ${INPUT_${@:check-%=%}} | ./dMagnetic -ini dMagnetic.ini -vmode "${@:check-%=%}" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | ${SHA256_CMD} | ${AWK_CMD} -F' ' '{ print $$1; }' - `" = ${CHECKSUM_${@:check-%=%}} ] ; then ${ECHO_CMD} "$@ OK" ; else ${ECHO_CMD} "$@ failed" ; exit 1 ; fi ############## invoke all the tests ############################################ check: ${CHECKS} @${ECHO_CMD} "***********************************************" @${ECHO_CMD} "Post-compilation tests for dMagnetic successful" @${ECHO_CMD} "***********************************************" do-test: check dmagnetic-0.32/dMagneticini.50000644000175000017500000002410414076357617015475 0ustar dettusdettus.\" Process this file with .\" groff -man -Tascii dMagneticini.5 .\" . .Dd July 19th, 2021 .Os OpenBSD .Dt dMagneticini 5 . .Sh NAME .Nm dMagnetic.ini .Nd Configuration file for dMagnetic. . . .Sh SHORTCUT Run dMagnetic -helpini to see an example. .Sh SYNOPSIS The configuration for .Xr dMagnetic 6 , and the location of the game binaries is stored in a .ini file. .Sh ELEMENTS The elements within the .ini files are .Bl -tag -width 10 .It Sections Sections are in square brackets. .Pp [FILE] .Pp [DEFAULTGUI] . .It Entries entries have a = in them. .Pp rows=40 .Pp pawnmag=/usr/local/share/games/magneticscrolls/pawn.mag . .It Comments ;This is a comment .Pp . Comments start with a ; .Pp To use a ; in an entry, it has to be preceded by a backslash \\; .br Consequently, using a backslash requires preceding it with a backslash as well. \\\\ .Pp .El .Sh SECTION [FILE] Like any .ini file, it is broken down into sections, each section contains entries. One section has to be [FILES]. It should contain the names of the game binaries, so that the game engine can find them. For example: .br [FILES] .br pawnmag=/usr/local/share/games/magneticscrolls/pawn.mag .br pawngfx=/home/games/magneticscrolls/pawn.gfx .br ;pawnmsdos=/home/games/magneticscrolls/msdosversions/PAWN .Pp The .mag and .gfx packages can be downloaded from https://msmemorial.if-legends.org/memorial.php, btw. If you are lucky enough to have a copy of the MSDOS version stored on your harddrive, you can provide the directory with the pawnmsdos entry. .Pp ;wonderlandtworsc=/games/magneticscrolls/wonderland/TWO.RSC .br ;guildtworsc=/games/magneticscrolls/MSC/GTWO.RSC .br ;corruptiontworsc=/games/magneticscrolls/MSC/CTWO.RSC .br ;fishtworsc=/games/magneticscrolls/MSC/FTWO.RSC .Pp "Wonderland", and the "Magnetic Scrolls Collections" used a different framework, called "Magnetic Windows". This framework utilized a collection of resource files. By providing the name and the location of the second resource file "TWO.RSC", the names of the other ones can be computed. .Pp Another option comes in the form of using .d64 images from the Commodore 64 release of the games. Those image files come in pairs, since the games occupied both sides of the floppy. A komma is used to separate those two. .br ;pawnd64=/games/d64/pawn1.d64, /games/d64/pawn2.d64 .br ;guildd64=/games/d64/guild1.d64, /games/d64/guild2.d64 .br ;jinxterd64=/games/d64/jinxter1.d64, /games/d64/jinxter2.d64 .br ;corruptiond64=/games/d64/corruption1.d64, /games/d64/corruption2.d64 .br ;fishd64=/games/d64/fish1.d64, /games/d64/fish2.d64 .br ;mythd64=/games/d64/myth.d64 .Pp One other option is to use .DSK images from the Amstrad/Schneider CPC releases of the games. Just as the D64 images, they have to be provided as a pair. .br ;pawnamstradcpc=/games/amstradcpc/pawn1.DSK, /games/amstradcpc/pawn2.DSK .br ;guildamstradcpc=/games/amstradcpc/guild1.DSK, /games/amstradcpc/guild2.DSK .br ;jinxteramstradcpc=/games/amstradcpc/jinxter1.DSK, /games/amstradcpc/jinxter2.DSK .br ;corruptionamstradcpc=/games/amstradcpc/corruption1.DSK, /games/amstradcpc/corruption2.DSK .br .Pp There is also the option to use .DSK images from the Spectrum releases of the games. This platform did not have the pictures, so the games were shipped in single floppies. Thus, only one DSK file is needed .br ;pawnspectrum=/games/spectrum/thepawn.DSK .br ;guildspectrum=/games/spectrum/theguildofthieves.DSK .br ;jinxterspectrum=/games/spectrum/jinxter.DSK .br ;corruptionspectrum=/games/spectrum/corruption.DSK .br ;fishspectrum=/games/spectrum/fish.DSK .br ;mythspectrum=/games/spectrum/myth.DSK .br .Pp To use ADF/ADL/ADFS images from the Acorn Archimedes releases, the parameters are as followed: .br ;pawnarchimedes=/games/archimedes/thepawn.ADF .br ;guildarchimedes=/games/archimedes/theguildofthieves.ADF .br ;jinxterarchimedes=/games/archimedes/jinxter.ADF .br ;corruptionarchimedes=/games/archimedes/corruption.ADF .br ;fisharchimedes=/games/archimedes/fish.ADF .br ;mytharchimedes=/games/archimedes/myth.ADF .br .Pp To use ATR images from the AtariXL/Atari800 releases, the parameters are as followed: .br ;pawnatarixl=/atr/PAWN1.ATR,/atr/PAWN2.ATR .br ;guildatarixl=/atr/GUILD1.ATR,/atr/GUILD2.ATR .br ;jinxteratarixl=/atr/JINXTER1.ATR,/atr/JINXTER2.ATR .br .Pp To use NIB,2MG or WOZ images from the Apple II releases, the parameters are as followed: .br ;pawnappleii=/nib/pawn.nib .br ;guildappleii=/2mg/guild.2mg .br ;jinxterappleii=/nib/jinxter1.nib,/nib/jinxter2.nib .br ;corruptionappleii=/woz/corruptA.woz,/woz/corruptB.woz,/woz/corruptC.woz .Pp To avoid any form of misunderstanding, it is recommended to make sure that the .ini file is providing only one of the five methods. The other four should be commented out. .Sh SECTION [RANDOM] This section configures the random number generator. .Pp .br mode=pseudo .br ;mode=real .br seed=12345 .Pp The mode can be one of two: "pseudo" or "real". Each one of them offers a different experience whilst playing. When testing the software, or playing with scripts, the "pseudo" option should be chosen. This offers a certain degree of determinsm. .br Legal values for seed are in the range of 1 to 2147483647. .Pp .Sh SECTION [DEFAULTGUI] This section is configuring the default output Interface. .Pp .br [DEFAULTGUI] .br rows=40 .br columns=100 .br ;align=left .br align=block .br ;align=right .br ;mode=none .br ;mode=monochrome .br ;mode=low_ansi .br ;mode=low_ansi2 .br mode=high_ansi .br ;mode=high_ansi2 .br ;mode=sixel .br low_ansi_characters=\\\\/=|\\; .br monochrome_characters= .:-=+*x#@$X .br sixel_resolution=1024x768 .br sixel_forceresolution=No .Pp Changing the numbers of rows will make pictures longer, changing the number of columns makes them wider. To change the output mode, comment it in, and comment the current one out. .br The characters that will be used in the low ansi rendering mode can be configured with the low_ansi_characters entry. Note that the backslash and semicolon need to be escaped. .br The monochrome_characters entry is a the actual shade that will be displayed. The leftmost part start with the lowest intensity, it goes up until the right. .br The sixel resolution is setting the amount of pixels in which the images are being rendered with sixel mode. .br Not forcing the resolution will render the pictures with the actual aspect ratio. .br .Sh EXAMPLE .Pp .br ;you can download the files from https://msmemorial.if-legends.org/magnetic.php .br [FILES] .br pawnmag=/usr/local/share/games/magneticscrolls/pawn.mag .br pawngfx=/usr/local/share/games/magneticscrolls/pawn.gfx .br ;pawnmsdos=/usr/local/share/games/magneticscrolls/msdosversions/PAWN .br ;pawnd64=/d64/PAWN1.d64,/d64/PAWN2.d64 .br ;pawnamstradcpc=/dsk/PAWN1.DSK,/dsk/PAWN2.DSK .br ;pawnspectrum=/dsk/PAWNspectrum.DSK .br ;pawnarchimedes=/adf/PAWNarchimedes.adf .br ;pawnatarixl=/atr/PAWN1.ATR,/atr/PAWN2.ATR .br ;pawnappleii=/nib/pawn.nib .br guildmag=/usr/local/share/games/magneticscrolls/guild.mag .br guildgfx=/usr/local/share/games/magneticscrolls/guild.gfx .br ;guildmsdos=/usr/local/share/games/magneticscrolls/msdosversions/GUILD .br ;guildtworsc=/usr/local/share/games/magneticscrolls/MSC/GTWO.RSC .br ;guild64=/d64/GUILD1.d64,/d64/GUILD2.d64 .br ;guildamstradcpc=/dsk/GUILD1.DSK,/dsk/GUILD2.DSK .br ;guildspectrum=/dsk/GUILDspectrum.DSK .br ;guildarchimedes=/adf/GUILDarchimedes.adf .br ;guildatarixl=/atr/GUILD1.ATR,/atr/GUILD2.ATR .br ;guildappleii=/nib/guild.nib .br jinxtermag=/usr/local/share/games/magneticscrolls/jinxter.mag .br jinxtergfx=/usr/local/share/games/magneticscrolls/jinxter.gfx .br ;jinxtermsdos=/usr/local/share/games/magneticscrolls/msdosversions/JINXTER .br ;jinxterd64=/d64/JINXTER1.d64,/d64/JINXTER2.d64 .br ;jinxteramstradcpc=/dsk/JINXTER1.DSK,/dsk/JINXTER2.DSK .br ;jinxterspectrum=/dsk/JINXTERspectrum.DSK .br ;jinxterarchimedes=/adf/JINXTERarchimedes.adf .br ;jinxteratarixl=/atr/JINXTER1.ATR,/atr/JINXTER2.ATR .br ;jinxterappleii=/nib/jinxter1.nib,/nib/jinxter2.nib .br corruptionmag=/usr/local/share/games/magneticscrolls/ccorrupt.mag .br corruptiongfx=/usr/local/share/games/magneticscrolls/ccorrupt.gfx .br ;corruptionmsdos=/usr/local/share/games/magneticscrolls/msdosversions/CORRUPT .br ;corruptiontworsc=/usr/local/share/games/magneticscrolls/MSC/CTWO.RSC .br ;corruptiond64=/d64/CORRUPT1.d64,/d64/CORRUPT2.d64 .br ;corruptionamstradcpc=/dsk/CORRUPTION1.DSK,/dsk/CORRUPTION2.DSK .br ;corruptionspectrum=/dsk/CORRUPTIONspectrum.DSK .br ;corruptionarchimedes=/adf/CORRUPTIONarchimedes.adf .br ;corruptionappleii=/woz/corruptA.woz,/woz/corruptB.woz,/woz/corruptC.woz .br fishmag=/usr/local/share/games/magneticscrolls/fish.mag .br fishgfx=/usr/local/share/games/magneticscrolls/fish.gfx .br ;fishmsdos=/usr/local/share/games/magneticscrolls/msdosversions/FISH .br ;fishtworsc=/usr/local/share/games/magneticscrolls/MSC/FTWO.RSC .br ;fishd64=/d64/FISH1.d64,/d64/FISH2.d64 .br ;fishamstradcpc=/dsk/FISH1.DSK,/dsk/FISH2.DSK .br ;fishspectrum=/dsk/FISHspectrum.DSK .br ;fisharchimedes=/adf/FISHarchimedes.adf .br mythmag=/usr/local/share/games/magneticscrolls/myth.mag .br mythgfx=/usr/local/share/games/magneticscrolls/myth.gfx .br ;mythmsdos=/usr/local/share/games/magneticscrolls/msdosversions/MYTH .br ;mythd64=/usr/local/share/games/magneticscrolls/MYTH.d64 .br ;mythamstradcpc=/dsk/MYTH1.DSK,/dsk/MYTH2.DSK .br ;mythspectrum=/dsk/MYTHspectrum.DSK .br ;mytharchimedes=/adf/MYTHarchimedes.adf .br wonderlandmag=/usr/local/share/games/magneticscrolls/wonder.mag .br wonderlandgfx=/usr/local/share/games/magneticscrolls/wonder.gfx .br ;wonderlandtworsc=/usr/local/share/games/magneticscrolls/WONDER/TWO.RSC .br [RANDOM] .br mode=pseudo .br ;mode=real .br seed=12345 .br [DEFAULTGUI] .br rows=40 .br columns=120 .br ;align=left .br align=block .br ;align=right .br ;mode=none .br ;mode=monochrome .br ;mode=monochrome_inv .br ;mode=low_ansi .br mode=low_ansi2 .br ;mode=high_ansi .br ;mode=high_ansi2 .br ;mode=sixel .br low_ansi_characters=\\/|=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ .br monochrome_characters= .-=+*x#@$X .br sixel_resolution=800x600 .br sixel_forceresolution=No .br .Pp .Sh BUGS Report bugs to .An Aq dettus@dettus.net . Make sure to include DMAGNETIC somewhere in the subject. .Sh AUTHOR Written by .An Thomas Dettbarn .Sh SEE ALSO .Xr dMagnetic 6 dmagnetic-0.32/src/0000755000175000017500000000000014076357617013602 5ustar dettusdettusdmagnetic-0.32/src/loader/0000755000175000017500000000000014076357622015044 5ustar dettusdettusdmagnetic-0.32/src/loader/loader_appleii.c0000644000175000017500000005220314076357617020167 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // the purpose of this file is to read the D64 disk image files // and to translate them into the .mag/.gfx format. #include #include #include #include "loader_appleii.h" #include "loader_common.h" #include "configuration.h" #include "vm68k_macros.h" #define PREAMBLE_SIZE 3 #define EPILOGUE_SIZE 3 #define ADDRBUF_SIZE 8 #define DATABUF_SIZE 343 #define MAXDISKS 3 #define MAXTRACKS 35 #define MAXSECTORS 16 #define SECTORBYTES 256 #define DSKTRACKSIZE (MAXSECTORS*SECTORBYTES) #define DSKSIZE (MAXTRACKS*MAXSECTORS*SECTORBYTES) #define NIBTRACKSIZE (MAXSECTORS*416) // TODO: why 416? #define GAME_PAWN 0 #define GAME_GUILD 1 #define GAME_JINXTER 2 #define GAME_CORRUPTION 3 #define MAXPICTURES 32 #define WOZ_MAXQUARTERTRACKS (4*MAXTRACKS) // TODO: not sure what to do with this... #define WOZ_BLOCKSIZE 512 // the woz format has been designed to work on SD cards. So blocks are aligned to 512 to speed up processing // the most important information, parsed from the WOZ header typedef struct _tWozInfo { int quarterTrack[WOZ_MAXQUARTERTRACKS]; // TODO: not sure what to do with this... int trackStart[WOZ_MAXQUARTERTRACKS]; // the block offset within the WOZ file int trackBits[WOZ_MAXQUARTERTRACKS]; // the number of bits for a track unsigned int crc32_expected; } tWozInfo; int loader_appleii_woz_parseheader(unsigned char* pWozBuf,int wozsize,tWozInfo *pWozInfo) { int idx; int len; unsigned char donemask; idx=0; donemask=0; while (idxcrc32_expected=READ_INT32BE(pWozBuf,idx+8); idx+=12; } else if (memcmp(&pWozBuf[idx],"INFO",4)==0) { len=READ_INT32LE(pWozBuf,idx+4); idx+=8; // skip the info chunk idx+=len; } else if (memcmp(&pWozBuf[idx],"TMAP",4)==0) { int i; len=READ_INT32LE(pWozBuf,idx+4); idx+=8; for (i=0;i=0 && xquarterTrack[i]=x; } } idx+=len; donemask|=1; } else if (memcmp(&pWozBuf[idx],"TRKS",4)==0) { int i; int idx2; len=READ_INT32LE(pWozBuf,idx+4); idx+=8; idx2=idx; for (i=0;itrackStart[i]=WOZ_BLOCKSIZE*READ_INT16LE(pWozBuf,idx2);idx2+=2; idx2+=2; //skip the block count pWozInfo->trackBits[i]=READ_INT32LE(pWozBuf,idx2);idx2+=4; } idx+=len; donemask|=2; } else if (memcmp(&pWozBuf[idx],"META",4)==0) { len=READ_INT32LE(pWozBuf,idx+4); idx+=8; // skip the info chunk idx+=len; } else { fprintf(stderr,"Unknown Tag in WOZ2 detected %02X %02X %02X %02X\n",pWozBuf[idx+0],pWozBuf[idx+1],pWozBuf[idx+2],pWozBuf[idx+3]); return -1; } } if (donemask!=3) { fprintf(stderr," Error parsing the WOZ header\n"); return -1; } return 0; } // when the woz bit stream is synchronized, it can be interpreted as a nib stream. int loader_appleii_woz_synchronize(unsigned char* trackbuf,unsigned char* wozbuf,int len) { int i; unsigned char byte; unsigned char bit; unsigned int reg; int addrcnt; int datacnt; int part_cnt; int outidx; reg=0; byte=0; bit=0; addrcnt=0; datacnt=0; part_cnt=0; i=0; outidx=0; for (i=0;i>(7-wozbit))&1; byte<<=1; byte|=bit; if (byte&0x80) // byte is synchronized when the highest bit is set. { reg<<=8; reg|=((unsigned int)byte)&0xff; reg&=0x00ffffff; if (part_cnt==0) { if (reg==0xD5AA96) // addr preamble found { addrcnt++; trackbuf[outidx++]=0xD5; // write the preamble trackbuf[outidx++]=0xAA; trackbuf[outidx++]=0x96; part_cnt=ADDRBUF_SIZE+EPILOGUE_SIZE; // collect 11 bytes } if (reg==0xD5AAAD) // data preamble found { datacnt++; trackbuf[outidx++]=0xD5; // write the preamble trackbuf[outidx++]=0xAA; trackbuf[outidx++]=0xAD; part_cnt=DATABUF_SIZE+EPILOGUE_SIZE; // collect 346 bytes } } else { trackbuf[outidx++]=byte; part_cnt--; } byte=0; } i++; } // at this point, the trackbuf contains the NIB stream, even though there is no padding between the sectors. // the nib decoder will be able to handle it, even though a physical drive might not be able to. return 0; } int loader_appleii_decode_addrbuf(unsigned char* pAddrBuf,unsigned char* volume,unsigned char* track,unsigned char* sector,unsigned char* checksum) { const unsigned char loader_appleii_deinterleave[16]={ 0x0,0x7,0xe,0x6,0xd,0x5,0xc,0x4,0xb,0x3,0xa,0x2,0x9,0x1,0x8,0xf }; int ridx; unsigned char x; unsigned char check; ridx=0; check=0; #define ROL(x) ((((x)&0x80)>>7|(x)<<1)&0xff) x=pAddrBuf[ridx++];x=ROL(x);x&=pAddrBuf[ridx++];*volume=x;check^=x; x=pAddrBuf[ridx++];x=ROL(x);x&=pAddrBuf[ridx++];*track=x;check^=x; x=pAddrBuf[ridx++];x=ROL(x);x&=pAddrBuf[ridx++];*sector=loader_appleii_deinterleave[x&0xf];check^=x; x=pAddrBuf[ridx++];x=ROL(x);x&=pAddrBuf[ridx++];*checksum=x;check^=x; if (check) { fprintf(stderr,"Warning. Checksum mismatch\n"); } return check; } int loader_appleii_decodenibtrack(unsigned char* pTrackBuf,int track,unsigned char* pDskBuf) { #define PREAMBLESIZE 3 #define DECODEROFFS 0x96 #define SECTORLSB 86 const unsigned char loader_appleii_addr_preamble[PREAMBLESIZE]={0xD5,0xAA,0x96}; const unsigned char loader_appleii_data_preamble[PREAMBLESIZE]={0xD5,0xAA,0xAD}; //const unsigned char loader_appleii_epilog[PREAMBLESIZE]={0xDE,0xAA,0xEB}; const unsigned char loader_appleii_translatetab[106]={ 0x00,0x01,0xFF,0xFF,0x02,0x03,0xFF,0x04, 0x05,0x06,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x07,0x08,0xFF,0xFF,0xFF,0x09,0x0A,0x0B, 0x0C,0x0D,0xFF,0xFF,0x0E,0x0F,0x10,0x11, 0x12,0x13,0xFF,0x14,0x15,0x16,0x17,0x18, 0x19,0x1A,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0x1B,0xFF,0x1C, 0x1D,0x1E,0xFF,0xFF,0xFF,0x1F,0xFF,0xFF, 0x20,0x21,0xFF,0x22,0x23,0x24,0x25,0x26, 0x27,0x28,0xFF,0xFF,0xFF,0xFF,0xFF,0x29, 0x2A,0x2B,0xFF,0x2C,0x2D,0x2E,0x2F,0x30, 0x31,0x32,0xFF,0xFF,0x33,0x34,0x35,0x36, 0x37,0x38,0xFF,0x39,0x3A,0x3B,0x3C,0x3D, 0x3E,0x3F}; unsigned char addr_track=0; unsigned char addr_sector=0; unsigned char addr_volume=0; unsigned char addr_checksum=0; int volumeid; int foundsectors; int ridx; int state; volumeid=-1; foundsectors=0; ridx=0; state=0; while (ridx<(NIBTRACKSIZE+SECTORBYTES+SECTORLSB+1+9*PREAMBLESIZE)) { switch(state) { case 0: // find the ADDR preamble { if ( pTrackBuf[(ridx+0)%NIBTRACKSIZE]==loader_appleii_addr_preamble[0] && pTrackBuf[(ridx+1)%NIBTRACKSIZE]==loader_appleii_addr_preamble[1] && pTrackBuf[(ridx+2)%NIBTRACKSIZE]==loader_appleii_addr_preamble[2]) { ridx+=PREAMBLE_SIZE; state=1; foundsectors++; } else ridx++; } break; case 1: // decode the ADDR data { unsigned char addrbuf[ADDRBUF_SIZE]; int i; for (i=0;i>=1; pDskBuf[widx]<<=1;pDskBuf[widx]|=(1&lsbbuf[j%SECTORLSB]);lsbbuf[j%SECTORLSB]>>=1; ridx++; } ridx+=PREAMBLESIZE; // skip over the epilogue state=0; // search for the next ADDR preamble } break; } } return volumeid; } int loader_appleii_mkgfx(unsigned char *gfxbuf,int* gfxsize,int gameid,int diskcnt,int *pDskOffs) { #define PICTURE_HOTFIX1 0x80000000 #define PICTURE_HOTFIX2 0x40000000 #define PICTURE_HOTFIX3 0x20000000 #define PICTURENUM 26 #define CODESECTIONS 5 #define TOTALSECTIONS (PICTURENUM+CODESECTIONS) unsigned int hotfix1=(1<<1)|(1<< 7); unsigned int hotfix2=(1<<2)|(1<<13); unsigned int hotfix3=(1<<16); if (gameid!=GAME_CORRUPTION) { *gfxsize=0; return 0; } if (diskcnt!=MAXDISKS) { fprintf(stderr,"wrong number of floppy disks\n"); return -1; } *gfxsize=4+4*32+diskcnt*DSKSIZE; { unsigned char mask; unsigned char byte; #define UNHUFFSTART 0x00a00 #define DIR_START 0x997 #define DIR_END 0x9d5 int outidx; int tracks[MAXPICTURES]; int sectors[MAXPICTURES]; int i; int cnt; int unhuffsize; unsigned char terminal; int treeoffs; int bitidx; int unhuffoffs; int treeidx=0; outidx=0; cnt=0; unhuffoffs=4+4*MAXPICTURES+pDskOffs[0]+UNHUFFSTART; terminal=0; unhuffsize=READ_INT16LE(gfxbuf,unhuffoffs); treeoffs=unhuffoffs+2; bitidx=treeoffs+16+16; treeidx=0; mask=0; byte=0; while (outidx>=1; if (branch&0x80) { treeidx=branch&0xf; } else { treeidx=0; terminal<<=4; terminal|=(branch&0xf); terminal&=0xff; if (outidx>=(DIR_START*2) && outidx<=(DIR_END*2) && ((outidx&1)==1)) { if (cnt>=1; hotfix2>>=1; hotfix3>>=1; WRITE_INT32BE(gfxbuf,4+i*4,offs); } } gfxbuf[0]='M';gfxbuf[1]='a';gfxbuf[2]='P';gfxbuf[3]='8'; return 0; } typedef struct _tSection { int track; int sector; int disk; int len; int scrambled; int rle; } tSection; int loader_appleii_readsection(unsigned char* pOut,tSection section,unsigned char* pDskBuf,int diskcnt,int* pDskOffs,int pivot) { int idx; int outidx; int firstsector; int rle; int rlecutoff=DSKSIZE; unsigned char tmp[SECTORBYTES]; unsigned char lc; int i; rle=section.rle; outidx=0; firstsector=1; idx=(section.track*MAXSECTORS+section.sector)*SECTORBYTES+pDskOffs[section.disk]; lc=0xff; while (outidxsection.len) outidx=section.len; return outidx; } int loader_appleii_mkmag(unsigned char* magbuf,int* magsize,int gameid,unsigned char* pDskBuf,int diskcnt,int* pDskOffs) { int magidx; int codesize; int stringidx0; int string1size; int string2size; int dictsize; int huffmantreeidx; int i; typedef struct _tGameInfo { int name_track; int name_sector; tSection code_section; tSection code2_section; int pivot_code2; tSection string1_section; tSection string2_section; tSection dict_section; int version; char gamename[21]; } tGameInfo; const tGameInfo loader_appleii_gameInfo[4]={ {0x01,0x0, {0x04,0x0,0,65536,1,0},{-1,-1,-1,-1,0,0}, -1,{0x12,0x0,0,0xc000,0,0}, {0x1e,0x0,0,0xb00,0,0}, {-1,-1,-1,0,0,0}, 0,"The Pawn"}, {0x00,0x9, {0x03,0x9,0,65536,1,1},{-1,-1,-1,-1,0,0}, -1,{0x12,0xb,0,0xf100,0,0}, {0x21,0xc,0,0xe00,0,0}, {-1,-1,-1,0,0,0}, 1,"The Guild of Thieves"}, {0x00,0x9, {0x08,0x2,0,0x3300,1,1},{0x00,0x0,1,0xcd00,1,0}, 7,{0x0c,0xc,1, 57344,0,0}, {0x1a,0xc,1, 24832,0,0}, {0x06,0x0,0, 8704,1,0}, 2,"Jinxter"}, {0x00,0x9, {0x04,0x0,0,0x4200,1,0},{0x00,0x0,1,0xbe00,1,0}, 2,{0x0b,0xe,1, 57344,0,0}, {0x19,0xe,1, 37120,0,0}, {0x08,0x2,0, 7680,1,0}, 3,"Corruption"} }; { int offs; int i; unsigned char c; printf("Detected '%s'\n",loader_appleii_gameInfo[gameid].gamename); offs=(loader_appleii_gameInfo[gameid].name_track*MAXSECTORS+loader_appleii_gameInfo[gameid].name_sector)*SECTORBYTES; offs+=3; i=0; c=0; printf("["); while (i<0x2c && c!=0xa9) { c=pDskBuf[pDskOffs[0]+offs]; i++; offs++; if (c>=' ' && c<127) printf("%c",c); } printf("]\n"); } magidx=42; codesize=loader_appleii_readsection(&magbuf[magidx],loader_appleii_gameInfo[gameid].code_section,pDskBuf,diskcnt,pDskOffs,0); codesize+=loader_appleii_readsection(&magbuf[magidx+codesize],loader_appleii_gameInfo[gameid].code2_section,pDskBuf,diskcnt,pDskOffs,loader_appleii_gameInfo[gameid].pivot_code2); magidx+=codesize; stringidx0=magidx; string1size=loader_appleii_readsection(&magbuf[magidx],loader_appleii_gameInfo[gameid].string1_section,pDskBuf,diskcnt,pDskOffs,0); magidx+=string1size; string2size=loader_appleii_readsection(&magbuf[magidx],loader_appleii_gameInfo[gameid].string2_section,pDskBuf,diskcnt,pDskOffs,0); magidx+=string2size; dictsize=loader_appleii_readsection(&magbuf[magidx],loader_appleii_gameInfo[gameid].dict_section,pDskBuf,diskcnt,pDskOffs,0); magidx+=dictsize; { int j; int matchcnt; huffmantreeidx=0; for (i=string1size;i=4) { huffmantreeidx=i; } } } if (gameid==GAME_CORRUPTION) for (i=0x212a;i<0x232a;i++) magbuf[i]=0; // finishing touches on corruption loader_common_addmagheader(magbuf,magidx,loader_appleii_gameInfo[gameid].version,codesize,string1size,string2size,dictsize,huffmantreeidx); *magsize=magidx; return 0; } int loader_appleii(char *appleiiname, char *magbuf,int* magsize, char *gfxbuf,int* gfxsize) { unsigned char* pDskBuf; char filename[1024]; unsigned char trackbuf[NIBTRACKSIZE]; int i,l; int j; int diskcnt; int volumeids[MAXDISKS]; int dskidx; int gameid; int diskoffs[MAXDISKS]; tWozInfo wozInfo; FILE *f; #define SIZE_NIBIMAGE 232960 #define SIZE_2MGIMAGE 143424 #define SIZE_DSKIMAGE 143360 pDskBuf=(unsigned char*)&gfxbuf[4+MAXPICTURES*4]; l=strlen(appleiiname); j=0; diskcnt=0; dskidx=0; for (i=0;i=0 && disknum #include #include #include "configuration.h" #include "vm68k_macros.h" #include "loader_msdos.h" #include "loader_mw.h" #include "loader_d64.h" #include "loader_dsk.h" #include "loader_archimedes.h" #include "loader_atarixl.h" #include "loader_appleii.h" typedef enum _eBinType { BINTYPE_NONE, BINTYPE_MAGGFX, BINTYPE_MSDOS, BINTYPE_TWORSC, BINTYPE_D64, BINTYPE_AMSTRADCPC, BINTYPE_SPECTRUM, BINTYPE_ARCHIMEDES, BINTYPE_ATARIXL, BINTYPE_APPLEII } eBinType; int loader_init(int argc,char** argv,FILE *f_inifile, char *magbuf,int* magsize, char* gfxbuf,int* gfxsize) { FILE *f; char magfilename[1024]; char gfxfilename[1024]; char binname[1024]; eBinType binType; int gamenamegiven; int n; int retval; binType=BINTYPE_NONE; retval=0; magfilename[0]=gfxfilename[0]=binname[0]=0; gamenamegiven=0; if ((retrievefromcommandline(argc,argv,"pawn",NULL,0)) || (retrievefromcommandline(argc,argv,"guild",NULL,0)) || (retrievefromcommandline(argc,argv,"jinxter",NULL,0)) || (retrievefromcommandline(argc,argv,"corruption",NULL,0)) || (retrievefromcommandline(argc,argv,"fish",NULL,0)) || (retrievefromcommandline(argc,argv,"myth",NULL,0)) || (retrievefromcommandline(argc,argv,"wonderland",NULL,0)) || (retrievefromcommandline(argc,argv,"wonder",NULL,0))) { gamenamegiven=1; } if (!f_inifile && gamenamegiven) { fprintf(stderr,"Game name was given, but no suitable .ini file found\n"); fprintf(stderr,"please run %s -helpini for more help\n",argv[0]); return 1; } { int i; char* gameprefix[]={"pawn","guild","jinxter","corruption","fish","myth","wonderland","wonder"}; char magname[32]; char gfxname[32]; char tworscname[32]; char msdosname[32]; char d64name[32]; char amstradcpcname[32]; char spectrumname[32]; char archimedesname[32]; char atarixlname[32]; char appleiiname[32]; for (i=0;i<8;i++) { snprintf(magname,32,"%smag",gameprefix[i]); snprintf(gfxname,32,"%sgfx",gameprefix[i]); snprintf(tworscname,32,"%stworsc",gameprefix[i]); snprintf(msdosname,32,"%smsdos",gameprefix[i]); snprintf(d64name,32,"%sd64",gameprefix[i]); snprintf(amstradcpcname,32,"%samstradcpc",gameprefix[i]); snprintf(spectrumname,32,"%sspectrum",gameprefix[i]); snprintf(archimedesname,32,"%sarchimedes",gameprefix[i]); snprintf(atarixlname,32,"%satarixl",gameprefix[i]); snprintf(appleiiname,32,"%sappleii",gameprefix[i]); if (retrievefromcommandline(argc,argv,gameprefix[i],NULL,0)) { magfilename[0]=gfxfilename[0]=0; if (retrievefromini(f_inifile,"[FILES]",magname,magfilename,sizeof(magfilename))&& retrievefromini(f_inifile,"[FILES]",gfxname,gfxfilename,sizeof(gfxfilename))) { binType=BINTYPE_MAGGFX; } else if (retrievefromini(f_inifile,"[FILES]",tworscname,binname,sizeof(binname))) { binType=BINTYPE_TWORSC; } else if (retrievefromini(f_inifile,"[FILES]",msdosname,binname,sizeof(binname))) { binType=BINTYPE_MSDOS; } else if (retrievefromini(f_inifile,"[FILES]",d64name,binname,sizeof(binname))) { binType=BINTYPE_D64; } else if (retrievefromini(f_inifile,"[FILES]",amstradcpcname,binname,sizeof(binname))) { binType=BINTYPE_AMSTRADCPC; } else if (retrievefromini(f_inifile,"[FILES]",spectrumname,binname,sizeof(binname))) { binType=BINTYPE_SPECTRUM; } else if (retrievefromini(f_inifile,"[FILES]",archimedesname,binname,sizeof(binname))) { binType=BINTYPE_ARCHIMEDES; } else if (retrievefromini(f_inifile,"[FILES]",atarixlname,binname,sizeof(binname))) { binType=BINTYPE_ATARIXL; } else if (retrievefromini(f_inifile,"[FILES]",appleiiname,binname,sizeof(binname))) { binType=BINTYPE_APPLEII; } } } } if (retrievefromcommandline(argc,argv,"-mag",magfilename,sizeof(magfilename))) { gfxfilename[0]=0; binname[0]=0; binType=BINTYPE_MAGGFX; } if (retrievefromcommandline(argc,argv,"-gfx",gfxfilename,sizeof(gfxfilename))) { binname[0]=0; binType=BINTYPE_MAGGFX; } if (retrievefromcommandline(argc,argv,"-msdosdir",binname,sizeof(binname))) { binType=BINTYPE_MSDOS; } if (retrievefromcommandline(argc,argv,"-tworsc",binname,sizeof(binname))) { binType=BINTYPE_TWORSC; } if (retrievefromcommandline(argc,argv,"-d64",binname,sizeof(binname))) { binType=BINTYPE_D64; } if (retrievefromcommandline(argc,argv,"-amstradcpc",binname,sizeof(binname))) { binType=BINTYPE_AMSTRADCPC; } if (retrievefromcommandline(argc,argv,"-spectrum",binname,sizeof(binname))) { binType=BINTYPE_SPECTRUM; } if (retrievefromcommandline(argc,argv,"-archimedes",binname,sizeof(binname))) { binType=BINTYPE_ARCHIMEDES; } if (retrievefromcommandline(argc,argv,"-atarixl",binname,sizeof(binname))) { binType=BINTYPE_ATARIXL; } if (retrievefromcommandline(argc,argv,"-appleii",binname,sizeof(binname))) { binType=BINTYPE_APPLEII; } switch (binType) { case BINTYPE_NONE: fprintf(stderr,"Please provide the game binaries\n");return -1;break; case BINTYPE_TWORSC: retval=loader_magneticwindows(binname,magbuf,magsize,gfxbuf,gfxsize);break; case BINTYPE_D64: retval=loader_d64(binname,magbuf,magsize,gfxbuf,gfxsize);break; case BINTYPE_AMSTRADCPC: retval=loader_dsk(binname,magbuf,magsize,gfxbuf,gfxsize,0); break; case BINTYPE_SPECTRUM: retval=loader_dsk(binname,magbuf,magsize,gfxbuf,gfxsize,1);break; case BINTYPE_ARCHIMEDES: retval=loader_archimedes(binname,magbuf,magsize,gfxbuf,gfxsize); break; case BINTYPE_ATARIXL: retval=loader_atarixl(binname,magbuf,magsize,gfxbuf,gfxsize); break; case BINTYPE_APPLEII: retval=loader_appleii(binname,magbuf,magsize,gfxbuf,gfxsize); break; case BINTYPE_MSDOS: retval=loader_msdos(binname,magbuf,magsize,gfxbuf,gfxsize); break; case BINTYPE_MAGGFX: retval=0; if (magfilename[0] && !gfxfilename[0]) { // deducing the name of the gfx file from the mag file int l; int found; found=0; l=strlen(magfilename); fprintf(stderr,"Warning! -mag given, but not -gfx. Deducing filename\n"); if (l>=4) { if (strncmp(&magfilename[l-4],".mag",4)==0) { memcpy(gfxfilename,magfilename,l+1); found=1; gfxfilename[l-4]='.'; gfxfilename[l-3]='g'; gfxfilename[l-2]='f'; gfxfilename[l-1]='x'; } } if (!found) { fprintf(stderr,"filename did not end in .mag (lower case)\n"); return 0; } } if (!magfilename[0] && gfxfilename[0]) { // deducing the name of the mag from the gfx int l; int found; found=0; l=strlen(gfxfilename); fprintf(stderr,"warning! -gfx given, but not -mag. Deducing filename\n"); if (l>=4) { if (strncmp(&gfxfilename[l-4],".gfx",4)==0) { memcpy(magfilename,gfxfilename,l+1); found=1; magfilename[l-4]='.'; magfilename[l-3]='m'; magfilename[l-2]='a'; magfilename[l-1]='g'; } } if (!found) { fprintf(stderr,"filename did not end in .gfx (lower case)\n"); return 0; } } if ((!magfilename[0] || !gfxfilename[0])) { return 1; } f=fopen(magfilename,"rb"); if (f==NULL) { fprintf(stderr,"ERROR: unable to open [%s]\n",magfilename); fprintf(stderr,"This interpreter needs a the game's binaries in the .mag and .gfx\n"); fprintf(stderr,"format from the Magnetic Scrolls Memorial website. For details, \n"); fprintf(stderr,"see https://msmemorial.if-legends.org/memorial.php\n"); return -2; } n=fread(magbuf,sizeof(char),*magsize,f); fclose(f); *magsize=n; f=fopen(gfxfilename,"rb"); if (f==NULL) { fprintf(stderr,"ERROR: unable to open [%s]\n",gfxfilename); fprintf(stderr,"This interpreter needs a the game's binaries in the .mag and .gfx\n"); fprintf(stderr,"format from the Magnetic Scrolls Memorial website. For details, \n"); fprintf(stderr,"see https://msmemorial.if-legends.org/memorial.php\n"); return -2; } n=fread(gfxbuf,sizeof(char),*gfxsize,f); *gfxsize=n; fclose(f); break; } // at this point, they are stored in magbuf and gfxbuf. { FILE *f; int finish; finish=0; if (retrievefromcommandline(argc,argv,"-dumpmag",magfilename,sizeof(magfilename))) { finish=1; printf("Writing new .mag file [%s]\n",magfilename); f=fopen(magfilename,"wb"); if (!f) { fprintf(stderr,"unable to open [%s]\n",magfilename); } fwrite(magbuf,sizeof(char),*magsize,f); fclose(f); } if (retrievefromcommandline(argc,argv,"-dumpgfx",gfxfilename,sizeof(gfxfilename))) { finish=1; printf("Writing new .gfx file [%s]\n",gfxfilename); f=fopen(gfxfilename,"wb"); if (!f) { fprintf(stderr,"unable to open [%s]\n",gfxfilename); } fwrite(gfxbuf,sizeof(char),*gfxsize,f); fclose(f); } if (finish) { printf("finishing now\n"); exit(0); } } return retval; } dmagnetic-0.32/src/loader/loader_dsk.c0000644000175000017500000005227114076357617017332 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // the purpose of this file is to read the Amstrad .DSK image files // as well as the .DSK images from the spectrum128/spectrum+3 // and to translate them into the .mag/.gfx format. #include #include #include #include "vm68k_macros.h" #include "loader_common.h" #include "loader_dsk.h" #define DSK_IMAGESIZE 194816 #define MAXFILENAMELEN (8+3) // filenames in CPM are 8 bytes long. with a 3 byte extension #define EXTENDLEN 4 // this part is 4 bytes long #define MAXBLOCKS 16 // 16 pointers per directory entry #define MAXBLOCKSIZE 1024 // one pointer is pointing towards 1 kByte #define MINSECTORSIZE 128 // the smallest number of bytes in a sector #define MAXOFFSETSPERENTRY ((MAXBLOCKSIZE/MINSECTORSIZE)*MAXBLOCKS) #define SIZE_FILEHEADER 256 #define SIZE_TRACKHEADER 256 #define SIZE_SECTORHEADER 8 #define SIZE_DIRENTRY 32 #define MAX_DIRENTRIES 32 // TODO??? #define MAX_SECTORNUMPERTRACK 64 #define MAX_SECTORNUMPERDISK (DSK_IMAGESIZE/MINSECTORSIZE) #define MAX_DISKS 2 #define NUM_GAMES 6 #define MAX_TRACKNUM 128 // TODO?? #define FILESUFFIX1 1 #define FILESUFFIX2 2 #define FILESUFFIX3 3 #define FILESUFFIX4 4 #define FILESUFFIX5 5 #define FILESUFFIX6 6 #define FILESUFFIX7 7 #define FILESUFFIX8 8 typedef struct _tGames { char gamename[32]; char gamefilename[MAXFILENAMELEN+1]; int version; unsigned short expectedsuffixes_amstradcpc; unsigned short expectedsuffixes_spectrum; } tGames; const tGames loader_dsk_knownGames[NUM_GAMES]={ // name,game file names, version number {"The Pawn", "PAWN\0 ",0,(1<>8))&0xff; outputbuf[i]^=key; } } int loader_dsk_readfile(unsigned char* inputbuf,unsigned char* outputbuf,int fileID,tDirEntry* pDirEntries,int entrycnt,int sectorsize) { int i; int outputidx; outputidx=0; for (i=0;i2) { fprintf(stderr,"Please provide no more than 2 filenames, separated by ,\n"); return -1; } } } // load the game for (i=0;i=MAX_SECTORNUMPERTRACK) { fprintf(stderr,"this file has too many sectors\n"); return -1; } // after the track header comes the sector header for (k=0;ksectorids[order[l]]) { // swap them order[k]^=order[l]; order[l]^=order[k]; order[k]^=order[l]; } } } for (k=0;k='0' && dirEntries[entrycnt].name[strlen(loader_dsk_knownGames[m].gamefilename)]<='8') ) { gamedetected=m; validfilename=1; } } } if (gamedetected!=-1) { int m; m=strlen(loader_dsk_knownGames[gamedetected].gamefilename); if (strncmp(dirEntries[entrycnt].name,loader_dsk_knownGames[gamedetected].gamefilename,m)==0) { dirEntries[entrycnt].fileID=dirEntries[entrycnt].name[m]-'0'; if (dirEntries[entrycnt].fileID>=0 && dirEntries[entrycnt].fileID<=8) { foundsuffixes|=(1<>5)&4; dirEntries[entrycnt].attrs|=(ptr[10]>>6)&2; dirEntries[entrycnt].attrs|=(ptr[11]>>7)&1; for (l=0;l>=1; } fprintf(stderr,"\n"); s=foundsuffixes&0x1fe; fprintf(stderr,"found "); for (i=0;i<9;i++) { if (s&1) fprintf(stderr,"%s%d ",loader_dsk_knownGames[gamedetected].gamefilename,i); s>>=1; } fprintf(stderr,"\n"); fprintf(stderr,"!! Some files missing. Sorry\n\n"); return 1; } if (amstrad0spectrum1) { retval=loader_dsk_spectrum_mag((unsigned char*)magbuf,magsize, dskimage,diskcnt, (unsigned char*)gfxbuf,2*DSK_IMAGESIZE, gamedetected, dirEntries,entrycnt,sectorsize); *gfxsize=0; } else { // start with the magbuf. use the first half of the empty gfx buf as tmpbuf for the unhuff retval=loader_dsk_amstradcpc_mag((unsigned char*)magbuf,magsize, dskimage,diskcnt, (unsigned char*)gfxbuf,2*DSK_IMAGESIZE, gamedetected, dirEntries,entrycnt,sectorsize); if (retval) return retval; retval=loader_dsk_amstradcpc_gfx((unsigned char*)gfxbuf,gfxsize, dskimage,diskcnt, gamedetected, dirEntries,entrycnt,sectorsize); } return retval; } dmagnetic-0.32/src/loader/loader_mw.h0000644000175000017500000000265214076357617017177 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LOADER_MW_H #define LOADER_MW_H int loader_magneticwindows(char* two_rsc, char *magbuf,int* magsize, char* gfxbuf,int* gfxsize); #endif dmagnetic-0.32/src/loader/loader_common.c0000644000175000017500000001321514076357617020034 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // the purpose of this file is to read the MS DOS Game binaries, // and to translate them into the .mag/.gfx format. #include #include #include #include "loader_common.h" #include "vm68k_macros.h" #define READ_INT24LE(ptr,idx) (\ (((unsigned int)((ptr)[((idx)+2)])&0xff)<<16) |\ (((unsigned int)((ptr)[((idx)+1)])&0xff)<< 8) |\ (((unsigned int)((ptr)[((idx)+0)])&0xff)<< 0) |\ 0) // most of the time, a huffman tree encoding was employed. // It would start with 1 byte treesize, then the branches in the order LRLRLRLR... (Left Right) // and then the bitstream. // within the tree, there are branches and terminal symbols. // the terminal symbols have bit 7 set, and are thus smaller than 8 bit. // to be able to encode full 8 bit bytes, 4 symbols are being combined into 3 bytes. int loader_common_unhuffer(unsigned char* input,int length,unsigned char* output) { int outputidx; unsigned char byte; unsigned char mask; int bitidx; int treesize; int treeidx; int threecnt; treeidx=0; treesize=input[0]; bitidx=3+treesize; // start decoding the bitstream directly after the tree threecnt=0; outputidx=0; mask=0; byte=0; while (bitidx>=1; if (branch&0x80) // the branch was a terminal symbol. { branch&=0x7f; if (threecnt==3) // this is the fourth symbol. distribute the bits within this symbol to the previous three. { output[outputidx-3]|=((branch>>4)&0x3)<<6; output[outputidx-2]|=((branch>>2)&0x3)<<6; output[outputidx-1]|=((branch>>0)&0x3)<<6; threecnt=0; } else { output[outputidx++]=branch; threecnt++; } treeidx=0; } else { treeidx=branch; // follow the branch } } return outputidx; } int loader_common_addmagheader(unsigned char* magbuf,int magsize,int version,int codesize,int string1size,int string2size,int dictsize,int huffmantreeidx) { if (huffmantreeidx==-1) { huffmantreeidx=string1size; } if (string1size>0x10000) { int x; x=string1size+string2size; string1size=0x10000; string2size=x-string1size; } magbuf[0]='M';magbuf[1]='a';magbuf[2]='S';magbuf[3]='c'; // 0.. 3: the magic word WRITE_INT32BE(magbuf, 4 ,magsize); // 4.. 7: the total size WRITE_INT32BE(magbuf, 8 ,42); // 8..11: the size of the header WRITE_INT16BE(magbuf,12 ,version); // 12..13: the version for the virtual machine WRITE_INT32BE(magbuf,14 ,codesize); // 14..17 the size of the game code WRITE_INT32BE(magbuf,18 ,string1size); // 18..21 the size of the string1 WRITE_INT32BE(magbuf,22 ,string2size); // 22..25 the size of the string2 WRITE_INT32BE(magbuf,26 ,dictsize); // 26..29 the size of the dictionary WRITE_INT32BE(magbuf,30 ,huffmantreeidx) // 30..33 the beginning of the huffman tree within the string buffer WRITE_INT32BE(magbuf,34 ,0); // 34..37: undo size WRITE_INT32BE(magbuf,38 ,0); // 38..41: undo pc return LOADER_OK; } #define BLOCKSIZE 256 #define MAXPIVOT 8 int loader_common_descramble(unsigned char* inptr,unsigned char* outptr,int pivot,unsigned char *lastchar,int rle) { unsigned char tmp[BLOCKSIZE]; int i; int j; int outcnt; unsigned char lc; pivot%=MAXPIVOT; lc=0xff; if (lastchar!=NULL) lc=*lastchar; outcnt=0; // step 1: reverse the input block for (i=0;ipivot) { tmp[i]^=tmp[i-(1+pivot)]; } // step 3: run length encoding. 00 is followed by the amount of zeros following if (lc==0 && rle) { for (j=0;j #include #include #include "loader_msdos.h" #include "loader_common.h" #include "configuration.h" #include "vm68k_macros.h" typedef enum _eGame { GAME_UNKNOWN, GAME_JINXTER, GAME_CORRUPTION, GAME_FISH, GAME_MYTH, GAME_PAWN, GAME_GUILD } eGame; typedef struct _tGameInfo { eGame game; char prefix[8]; // the prefix for the game's binaries. int disk1size; // the size of the DISK1.PIX file is in an indicator for the game being used. int version; // the interpreter version. } tGameInfo; // The others have some opcodes which I can not decode (yet) #define KNOWN_GAMES 6 const tGameInfo gameInfo[KNOWN_GAMES]={ {GAME_PAWN, "PAWN", 209529,0}, // THE PAWN {GAME_GUILD, "GUILD", 185296,1}, // THE GUILD OF THIEVES {GAME_JINXTER, "JINX", 159027,2}, // JINXTER {GAME_CORRUPTION, "CORR", 160678,3}, // CORRUPTION {GAME_FISH, "FILE", 162541,3}, // FISH {GAME_MYTH, "FILE", 67512,3} // MYTH }; int loader_huffman_unpack(char* magbuf,int* decodedbytes,FILE *f) { unsigned char huffsize; unsigned char hufftab[256]; int huffidx; unsigned short todo; unsigned char byte; unsigned char mask; unsigned char tmp[3]; int threebytes; int n; // this variable is getting rid of some compiler warnings. int magidx; magidx=0; n=fread(&huffsize,sizeof(char),1,f); if (huffsize==0x49) { magbuf[0]=(char)huffsize;magidx=1; magidx+=fread(&magbuf[1],sizeof(char),(1<<20),f); } else { n+=fread(hufftab,sizeof(char),huffsize,f); n+=fread(&todo,sizeof(short),1,f); // what are those two bytes? mask=0; huffidx=0; threebytes=0; while (!feof(f) || mask) { unsigned char branchr,branchl; unsigned char branch; if (mask==0) { n+=fread(&byte,sizeof(char),1,f); mask=0x80; } branchl=hufftab[2*huffidx+0]; branchr=hufftab[2*huffidx+1]; branch=(byte&mask)?branchl:branchr; if (branch&0x80) // the highest bit signals a terminal symbol. { huffidx=0; branch&=0x7f; // the terminal symbols from the tree are only 6 bits wide. // to extend this to 8 bits, the fourth symbol contains the // two MSB for the previous three: // 00AAAAAA 00BBBBBB 00CCCCCC 00aabbcc if (threebytes==3) { int i; threebytes=0; for (i=0;i<3;i++) { // add the 2 MSB to the existing 6. tmp[i]|=((branch<<2)&0xc0); // MSB first magbuf[magidx]|=(char)tmp[i]; magidx++; branch<<=2; // MSB first } } else { tmp[threebytes]=branch; threebytes++; } } else { huffidx=branch; } mask>>=1; } } *decodedbytes=magidx; return (n==0); } int loader_msdos(char* msdosdir, char *magbuf,int* magsize, char* gfxbuf,int* gfxsize) { #define OPENFILE(filename) \ f=fopen((filename),"rb"); \ if (!f) \ { \ fprintf(stderr,"ERROR. Unable to open %s. Sorry.\n",(filename)); \ return -1; \ } FILE *f; char filename[1024]; int i; int gameID; int magsize0; int gfxsize0; char postfix; magsize0=*magsize; gfxsize0=*gfxsize; *magsize=0; *gfxsize=0; // clear the headers. memset(gfxbuf,0,16); memset(magbuf,0,42); postfix=0; { int sizedisk1,sizedisk2,sizeindex; /////////////////////// GFX packing // the header of the GFX is always 16 bytes. // values are stored as BigEndians // 0.. 3 are the magic word 'MaP3' // 4.. 7 are the size of the GAME4 (index) file (always 256) // 8..11 are the size of the DISK1.PIX file // 12..15 are the size of the DISK2.PIX file // then the INDEX file (beginning at 16) // then the DISK1.PIX file // then the DISK2.PIX file // step 1: find out which game it is. snprintf(filename,1024,"%s/DISK1.PIX",msdosdir); OPENFILE(filename); sizedisk1=fread(&gfxbuf[16+256],sizeof(char),gfxsize0-16-256,f); fclose(f); gameID=-1; for (i=0;i=KNOWN_GAMES) { fprintf(stderr,"ERROR: Unable to recognize game\n"); return -2; } // step 2: read the binary files, and store them in the gfxbuffer snprintf(filename,1024,"%s/DISK2.PIX",msdosdir); f=fopen(filename,"rb"); if (!f) // MYTH does not have a second disc. { sizedisk2=0; } else { sizedisk2=fread(&gfxbuf[16+256+sizedisk1],sizeof(char),gfxsize0-sizedisk1-16-256,f); fclose(f); } // some versions have filenames ending in a . { snprintf(filename,1024,"%s/%s4%c",msdosdir,gameInfo[gameID].prefix,postfix); f=fopen(filename,"rb"); if (!f) postfix='.'; else fclose(f); } snprintf(filename,1024,"%s/%s4%c",msdosdir,gameInfo[gameID].prefix,postfix); OPENFILE(filename); sizeindex=fread(&gfxbuf[16],sizeof(char),256,f); fclose(f); // step 3: add the header to the gfx buffer gfxbuf[0]='M';gfxbuf[1]='a';gfxbuf[2]='P';gfxbuf[3]='3'; WRITE_INT32BE(gfxbuf, 4,sizeindex); WRITE_INT32BE(gfxbuf, 8,sizedisk1); WRITE_INT32BE(gfxbuf,12,sizedisk2); *gfxsize=16+sizeindex+sizedisk1+sizedisk2; } ////////////////////////// done with GFX packing ////////////////////////// MAG packing { int codesize=0; int dictsize=0; int string1size=0; int string2size=0; int magidx=0; magidx=42; // the program for the 68000 machine is stored in the file ending with 1. snprintf(filename,1024,"%s/%s1%c",msdosdir,gameInfo[gameID].prefix,postfix); OPENFILE(filename); // sometimes, this file is packed. // this can be checked quite easily, because in the other cases it starts with the instruction // 49FA= LEA. // if (loader_huffman_unpack(&magbuf[magidx],&codesize,f)) return -1; fclose(f); magidx+=codesize; // the strings for the game are stored in the files ending with 3 and 2 snprintf(filename,1024,"%s/%s3%c",msdosdir,gameInfo[gameID].prefix,postfix); OPENFILE(filename); string1size=fread(&magbuf[magidx],sizeof(char),magsize0-magidx,f); fclose(f); magidx+=string1size; snprintf(filename,1024,"%s/%s2%c",msdosdir,gameInfo[gameID].prefix,postfix); OPENFILE(filename); string2size=fread(&magbuf[magidx],sizeof(char),magsize0-magidx,f); fclose(f); magidx+=string2size; if (gameInfo[gameID].version>=2) { // dictionaries are packed. dictsize=0; snprintf(filename,1024,"%s/%s0%c",msdosdir,gameInfo[gameID].prefix,postfix); OPENFILE(filename); if (loader_huffman_unpack(&magbuf[magidx],&dictsize,f)) return -1; fclose(f); magidx+=dictsize; } // TODO: what about the 5? if (gameInfo[gameID].game==GAME_MYTH && magbuf[0x314a]==0x66) magbuf[0x314a]=0x60; // final touch loader_common_addmagheader((unsigned char*)magbuf,magidx,gameInfo[gameID].version,codesize,string1size,string2size,dictsize,-1); *magsize=magidx; } return 0; } dmagnetic-0.32/src/loader/loader_atarixl.c0000644000175000017500000002741214076357617020214 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // the purpose of this file is to read the MS DOS Game binaries, // and to translate them into the .mag/.gfx format. #include #include #include #include "loader_common.h" #include "loader_atarixl.h" #include "vm68k_macros.h" #define LOADER_ATARIXL_OK 0 #define LOADER_ATARIXL_NOK -1 typedef struct _tGameInfo { unsigned char name[22]; unsigned char version; // 0=The Pawn. 1=The Guild of Thieves. 2=Jinxter unsigned int offs_code1; // the offset of the first code section unsigned int offs_code2; // the offset to the second code section unsigned int offs_string1; // the offset to the string1 section unsigned int offs_string2; // the offset to the string2 section unsigned int offs_dict; // the offset to the dict section unsigned int offs_pictures[30]; // the offset to the pictures within the .ATR files. } tGameInfo; #define DISK_SIZE 133136 #define DISK_OFFSETMASK 0x3ffff #define DISK1_FLAG 0x40000 #define DISK2_FLAG 0x80000 // i was not able to find the proper directory. // but i was able to find the code/string/dict sections through correlations and other means. // this is how the following table has been created. #define GAMENUM 3 const tGameInfo loader_atarixl_cGameInfo[GAMENUM]={ {"The Pawn",0, 0x3990|DISK1_FLAG,0,0x11310|DISK1_FLAG,0x1c710|DISK1_FLAG,0, { DISK2_FLAG|0x00010,DISK2_FLAG|0x00a90,DISK2_FLAG|0x01a10,DISK2_FLAG|0x02410, DISK2_FLAG|0x1d190,DISK2_FLAG|0x03310,DISK2_FLAG|0x04110,DISK2_FLAG|0x04a10, DISK2_FLAG|0x05210,DISK2_FLAG|0x05890,DISK2_FLAG|0x1f910,DISK2_FLAG|0x06390, DISK2_FLAG|0x11310,DISK2_FLAG|0x11f10,DISK2_FLAG|0x12e90,DISK2_FLAG|0x13990, DISK2_FLAG|0x14190,DISK2_FLAG|0x14f90,DISK2_FLAG|0x15790,DISK2_FLAG|0x1ef10, DISK2_FLAG|0x06f90,DISK2_FLAG|0x16510,DISK2_FLAG|0x17010,DISK2_FLAG|0x1dd90, DISK2_FLAG|0x1e510,DISK2_FLAG|0x18010,DISK2_FLAG|0x18600,DISK2_FLAG|0x18f90, DISK2_FLAG|0x19e10,DISK2_FLAG|0x1a610 } }, {"The Guild Of Thieves",1, 0x3890|DISK1_FLAG,0x10|DISK2_FLAG,0xc010|DISK2_FLAG,0x1b110|DISK2_FLAG,0, { DISK1_FLAG|0x1b310,DISK1_FLAG|0x09690,DISK1_FLAG|0x14210,DISK2_FLAG|0x1be90, DISK1_FLAG|0x11d90,DISK1_FLAG|0x08990,DISK1_FLAG|0x10090,DISK1_FLAG|0x17490, DISK1_FLAG|0x11110,DISK1_FLAG|0x08190,DISK2_FLAG|0x1e010,DISK1_FLAG|0x0d610, DISK1_FLAG|0x0ed10,DISK1_FLAG|0x18490,DISK1_FLAG|0x1f710,DISK2_FLAG|0x1d190, DISK1_FLAG|0x0c690,DISK1_FLAG|0x0e010,DISK1_FLAG|0x1ea10,DISK1_FLAG|0x16c90, DISK1_FLAG|0x0aa10,DISK1_FLAG|0x15910,DISK1_FLAG|0x0cc10,DISK1_FLAG|0x09f90, DISK1_FLAG|0x0ba10,DISK1_FLAG|0x19210,DISK1_FLAG|0x1a810,DISK1_FLAG|0x12b90, DISK1_FLAG|0x14d90,0 } }, {"Jinxter",2, 0x3790|DISK1_FLAG,0x10|DISK2_FLAG,0xc710|DISK2_FLAG,0x1a710|DISK2_FLAG,0x6490|DISK1_FLAG, { DISK1_FLAG|0x08690,DISK1_FLAG|0x09990,DISK1_FLAG|0x0a690,DISK1_FLAG|0x0b390, DISK1_FLAG|0x0c490, 0, 0,DISK1_FLAG|0x0d840, DISK1_FLAG|0x0e690, 0,DISK1_FLAG|0x0f210,DISK1_FLAG|0x10090, DISK1_FLAG|0x10f10,DISK1_FLAG|0x11d10,DISK1_FLAG|0x12c10,DISK1_FLAG|0x13910, DISK1_FLAG|0x14590,DISK1_FLAG|0x15290,DISK1_FLAG|0x15d10, 0, DISK1_FLAG|0x16d90,DISK1_FLAG|0x17f90,DISK1_FLAG|0x18810,DISK1_FLAG|0x19590, DISK1_FLAG|0x1a410,DISK1_FLAG|0x1b910,DISK1_FLAG|0x1c510, 0, DISK1_FLAG|0x1ce90, 0 } } }; #define BLOCKSIZE 256 #define MAXPIVOT 8 #define GETIDX(idx,offset,disk1offs,disk2offs) \ idx=(offset); \ if ((idx)&DISK1_FLAG) idx=((idx)&DISK_OFFSETMASK)+(disk1offs); \ if ((idx)&DISK2_FLAG) idx=((idx)&DISK_OFFSETMASK)+(disk2offs); int loader_atarixl_detectgame(unsigned char* diskbuf,int* disk1offs,int* disk2offs) { int d1,d2; int found; int i; unsigned char tmp[256]; d1=*disk1offs; d2=*disk2offs; // the game code always starts with the same instruction 49FA FFFE. // in each game release, this is at a different postion within the // floppy disks. this can be used to determine which game it is. // since the game code has been scrambled, it needs to be descrambled first. // first, assume that the first argument was also the first floppy disk found=-1; for (i=0;iversion!=0) { magidx-=2; rle=1; } pivot=0; lc=0xff; GETIDX(idx,pGameInfo->offs_code1,disk1offs,disk2offs); n=loader_common_descramble(&diskbuf[idx],&magbuf[magidx],pivot,&lc,rle); if (pGameInfo->version!=0) { codeleft=READ_INT16BE(magbuf,magidx); } code1size+=n; idx+=BLOCKSIZE; magidx+=n; while (codeleft>=BLOCKSIZE) { pivot=(pivot+1)%MAXPIVOT; n=loader_common_descramble(&diskbuf[idx],&magbuf[magidx],pivot,&lc,rle); codeleft-=BLOCKSIZE; idx+=BLOCKSIZE; magidx+=n; code1size+=n; } if (codeleft!=0) { codeleft=0x100-codeleft; code1size-=(codeleft+2); magidx-=codeleft; } GETIDX(idx,pGameInfo->offs_code2,disk1offs,disk2offs); codeleft=0x10000-code1size; while (codeleft>0) { pivot=(pivot+1)%MAXPIVOT; n=loader_common_descramble(&diskbuf[idx],&magbuf[magidx],pivot,&lc,0); codeleft-=BLOCKSIZE; idx+=BLOCKSIZE; magidx+=n; code2size+=n; } } // the strings are not scrambled. they are being copied over from the disk buffer into the .mag buffer. string1size=0; string2size=0; { int idx1; int idx2; int nxtdisk; int magidx0; magidx0=magidx; GETIDX(idx1,pGameInfo->offs_string1,disk1offs,disk2offs); GETIDX(idx2,pGameInfo->offs_string2,disk1offs,disk2offs); nxtdisk=disksize; if (idx1version==0) { huffmantreeidx=string1size; } else { int i; huffmantreeidx=0; // the huffman tree starts with the sequence 01 02 03 ?? 05. for (i=magidx0;i=0x10000) { int x; x=string1size+string2size; string1size=0x10000; string2size=x-string1size; } } dictsize=0; if (pGameInfo->offs_dict!=0) { int n; int idx; int pivot; unsigned char lc; GETIDX(idx,pGameInfo->offs_dict,disk1offs,disk2offs); pivot=0; while (dictsize<8704) // TODO: magic number { lc=0xff; n=loader_common_descramble(&diskbuf[idx],&magbuf[magidx],pivot,&lc,0); magidx+=n; dictsize+=n; pivot=(pivot+1)%MAXPIVOT; idx+=BLOCKSIZE; } } loader_common_addmagheader(magbuf,magidx,pGameInfo->version,code1size+code2size,string1size,string2size,dictsize,huffmantreeidx); *magbufsize=magidx; return LOADER_ATARIXL_OK; } int loader_atarixl_mkgfx(unsigned char* gfxbuf,int *gfxbufsize,int disk1offs,int disk2offs,const tGameInfo* pGameInfo) { int i; int idx; int gfxidx; // i am lazy // just translate the pre-determined offsets into the gfx buffer index gfxidx=0; gfxbuf[gfxidx++]='M'; gfxbuf[gfxidx++]='a'; gfxbuf[gfxidx++]='P'; gfxbuf[gfxidx++]='7'; for (i=0;i<30;i++) { GETIDX(idx,pGameInfo->offs_pictures[i],disk1offs,disk2offs); WRITE_INT32BE(gfxbuf,gfxidx,idx);gfxidx+=4; } // that's it return LOADER_ATARIXL_OK; } int loader_atarixl(char* atarixlname, char* magbuf,int* magbufsize, char* gfxbuf,int* gfxbufsize) { int disk1offs; int disk2offs; int disksize; int detectedgame; FILE *f; char* diskfile1; char* diskfile2; int i; diskfile1=atarixlname; diskfile2=atarixlname; for (i=0;i #include #include #include "loader_d64.h" #include "loader_common.h" #include "configuration.h" #include "vm68k_macros.h" #define D64_IMAGESIZE 174848 #define D64_TRACKNUM 40 #define D64_SECTORSIZE 256 #define D64_MAXENTRIES 64 #define D64_BITMASKSIZE 6080 // pictures have a resolution of 160x152 pixels typedef enum _eGame { GAME_UNKNOWN, GAME_JINXTER, GAME_CORRUPTION, GAME_FISH, GAME_MYTH, GAME_PAWN, GAME_GUILD } eGame; typedef enum _eFileType { TYPE_UNKNOWN, TYPE_CODE1, TYPE_CODE2, TYPE_CODE1_ENCRYPTED, TYPE_CODE2_ENCRYPTED, TYPE_STRING1, TYPE_STRING2, TYPE_DICTIONARY, TYPE_PICTURE, TYPE_CAMEO } eFileType; typedef struct _tFileEntry { unsigned int offset; // where in the image is the file? unsigned char track; // track unsigned char sector; // sector int len; // do not trust this one int side; // A, B or both? eFileType fileType; } tFileEntry; typedef struct _tGameInfo { eGame game; // the enumeration for the game int sides; // how many floppy sides did this game occupy? char magicword[5]; // the word, that is hidden in the second sector of the disk image int version; // the virtual machine's version char name[32]; // human readable signed char order[32]; // the order in which the pictures can be found in the images are not the same as in other releases. } tGameInfo; const tGameInfo loader_d64_gameinfo[6]= { // the pictures are ordered different from the other releases. there, this list would have been 0 1 2 3 4... // but the C64 had only a limited amount of floppy disk space. {GAME_JINXTER ,2,"ARSE",2,"Jinxter", { 4, 0, 5, 6, 7,-1, 8, 1, 9,10,11,12, 13,14,15,16, 17, 2, 3,27, 18,19,20,21, 22,23,24,25, 26,27,26,26}}, {GAME_CORRUPTION,2,"COKE",3,"Corruption", { 24, 8, 9,25, 10,13,15,16, 17, 1,18,23, 21, 6, 5, 4, 12,14, 2, 3, 11,20, 7,22, 19, 0,-1,-1, -1,-1,-1,-1 }}, {GAME_FISH ,2,"GLUG",3,"Fish!", { 3,21, 8,11, 18,16,17, 4, 2, 5, 1, 6, 9,10,14,20, 22,24,25, 0, 15,23, 7,19, 13,-1,26,-1, -1,-1,-1,-1 }}, {GAME_MYTH ,1,"GODS",3,"Myth", { 0, 1, 2, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}}, {GAME_PAWN ,2,"PAWN",0,"The Pawn", { 4,26,13,23, 0, 8,29, 5, 18,19, 3, 9, 12,11,16,22, 17,21,28, 6, 27,25,24, 2, 1,20,14, 7, 15,10,-1,-1 }}, {GAME_GUILD ,2,"SWAG",1,"The Guild of Thieves", { 9,17,20, 0,26,19,11,12, 4, 5, 2,13,14, 8, 6, 1,15,16, 3,24,21,28,22,25,18,23, 7,10,27,-1,-1,-1}}, }; int loader_d64_detectgame(unsigned char* diskram) { // find the magic word int i; char tmp[5]; #define MAGIC_WORD_LOCATION 0x1eb // at those positions in the image, a magic word is hidden. // it can be used to detect the game tmp[0]=diskram[MAGIC_WORD_LOCATION+0]; tmp[1]=diskram[MAGIC_WORD_LOCATION+1]; tmp[2]=diskram[MAGIC_WORD_LOCATION+2]; tmp[3]=diskram[MAGIC_WORD_LOCATION+3]; tmp[4]=0; for (i=0;i<6;i++) { if (strncmp(tmp,loader_d64_gameinfo[i].magicword,4)==0) return i; } return -1; } const unsigned char loader_d64_sectorcnt[D64_TRACKNUM]= { 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, // track 1-17 19,19,19,19,19,19,19, // track 18-24 18,18,18,18,18,18, // track 25-30 17,17,17,17,17,17,17,17,17,17 // track 31-40 }; void loader_d64_readEntries(unsigned char* d64image,int d64size,tFileEntry* pEntries,int* pEntrynum) { int i; int offsets[40]; int cnt; // first step: calculate the offsets for the tracks offsets[0]=0; for (i=1;iD64_IMAGESIZE) { return; } for (i=0;i<256;i++) buf[i]=d64image[offset++]; } void loader_d64_identifyEntries(unsigned char* d64image,tFileEntry* pEntries,int entryNum) { int i; unsigned char tmp1[256]; unsigned char tmp2[256]; int sideoffsets[4]={-1,-1,-1,-1}; // some file types have a fixed position in the list pEntries[2].fileType=TYPE_STRING1; pEntries[3].fileType=TYPE_STRING2; pEntries[4].fileType=TYPE_CAMEO; // first: find the code block for (i=0;i=D64_IMAGESIZE) offset=D64_IMAGESIZE; track=pEntries[i].track; sect =pEntries[i].sector; len =pEntries[i].len; } } rle=0; inputsize=len*D64_SECTORSIZE; for (i=0;i=D64_IMAGESIZE) offset=D64_IMAGESIZE; track=pEntries[i].track; sect =pEntries[i].sector; len =pEntries[i].len; } } outcnt=0; for (i=0;i=D64_IMAGESIZE) { offset=D64_IMAGESIZE; } else { offset=0; } for (j=0;j2) { fprintf(stderr,"Please provide no more than 2 filenames, separated by ,\n"); return -1; } } } // load the game for (i=0;i=D64_IMAGESIZE) offset=D64_IMAGESIZE; else offset=0; len =entries[i].len; picoffs[piccnt]=gfxidx; for (j=0;j #include #include #include "loader_common.h" #include "vm68k_macros.h" #define READ_INT24LE(ptr,idx) (\ (((unsigned int)((ptr)[((idx)+2)])&0xff)<<16) |\ (((unsigned int)((ptr)[((idx)+1)])&0xff)<< 8) |\ (((unsigned int)((ptr)[((idx)+0)])&0xff)<< 0) |\ 0) /////////////////////////////////////////// #define ADFS_IMAGESIZE 819200 #define ADFS_NICKMAP_OFFSET 0x40 #define ADFS_NICKMAP_END 0x400 #define RECURSIVEMAX 5 typedef struct _tGames { char magicdirname[10]; char version; char gamename[32]; unsigned int expectedmask; unsigned char pictureorder[32]; } tGames; #define MAXGAMES 5 #define MAXFILENAMENUM 10 #define F6CODE 6 #define F7DICT 7 #define F8STRING2 8 #define F9STRING1 9 #define F10PICTURES 10 const tGames loader_archimedes_cGames[MAXGAMES]={ {"Pawn", 0,"The Pawn", (1<>=8; indfound=0; // go through the allocation map, search for the indicator starti=-1; while (i='0' && name[1]<='9') { num*=10; num+=name[1]-'0'; } else num=-1; if (name[2]>='0' && name[2]<='9') { num*=10; num+=name[2]-'0'; } else if (name[2]!=0xd && name[2]!=0x00) num=-1; if (num>=0 && num<=MAXFILENAMENUM) { pOffsets[num]=offset; pLengths[num]=length; found|=(1<' ') match=0; } if (match) { *pGameId=j; } } return found; } return retval; } unsigned int loader_archimedes_findfiles(unsigned char* dskimg,int dsksize, int *pGameId,int* pOffsets,int* pLengths) // pOffsets are pointers to the files F6 (code), F7 (dict), f8 (string2), f9(string1), F10 (pictures). IN THAT ORDER. the same goes for the lengths { int hugo0nick1; int sectorsize; int indlen; int bytespermapbit; int diridx; unsigned int filemask; if (dsksize!=ADFS_IMAGESIZE) { fprintf(stderr,"The diskiamge size is off: %d was expected, but %d bytes were read\n",ADFS_IMAGESIZE,dsksize); return LOADER_NOK; } // step 1: find out, if the disk image contains a file system in the 'Hugo' or 'Nick' format. hugo0nick1=-1; // do it the lazy way. Just check if at the position where the root directory is expected to be, // the magic word "Hugo" or "Nick" appears. #define HUGOOFFS 0x400 #define NICKOFFS 0x800 // for Hugo disks, this is at the beginning of sector1, so @0x401-0x404 if (dskimg[HUGOOFFS+1]=='H' && dskimg[HUGOOFFS+2]=='u' && dskimg[HUGOOFFS+3]=='g' && dskimg[HUGOOFFS+4]=='o') hugo0nick1=0; // for the Nick disks, this is at the beginning of sector2, so 0x801-0x803 else if (dskimg[NICKOFFS+1]=='N' && dskimg[NICKOFFS+2]=='i' && dskimg[NICKOFFS+3]=='c' && dskimg[NICKOFFS+4]=='k') hugo0nick1=1; else { fprintf(stderr,"unable to determine the file system type.\n"); return LOADER_NOK; } sectorsize=1024; indlen=-1; bytespermapbit=-1; diridx=0x400; // step 2: if it is the "Nick" format, it comes with a header if (hugo0nick1==1) { int rootind; // according to //http://www.riscos.com/support/developers/prm/filecore.html#85862 // the header for the "Nick" format is in the first 36 bytes of the dsk image: // since the archimedes had an ARM processor, the values are little endian // 0..3: 4 bytes UNKNOWN // 4: 1 byte log2sector size * // 5: 1 byte sectors/track // 6: 1 bytes heads/track // 7: 1 byte density // 8: 1 byte indlen * // 9: 1 byte byterspermapbit * // 10: 1 byte skey // 11: 1 byte boot option // 12: 1 byte lowsector // 13: 1 byte number of zones // 14: 2 byes zone spare // 16: 4 bytes root indicator * // 20: 4 bytes disk size // 24: 4 bytes disk id // 26..35: disk name // only the ones with the * are important sectorsize=1< ",loader_archimedes_cGames[*pGameId].gamename); if ((filemask&loader_archimedes_cGames[*pGameId].expectedmask)!=loader_archimedes_cGames[*pGameId].expectedmask) { fprintf(stderr,"FAIL\n"); fprintf(stderr,"insufficient files on the disk. sorry\n"); return LOADER_NOK; } fprintf(stderr,"okay\n"); return LOADER_OK; } return LOADER_NOK; } int loader_archimedes_mkmag(unsigned char *dskimg,int dsksize,unsigned char* magbuf,int* magsize, int gameId,int* offsets,int *lengths) { int magidx; int codesize; int string1size; int string2size; int dictsize; magidx=42; // the game code is stored in F6, it is packed codesize=loader_common_unhuffer(&dskimg[offsets[F6CODE]],lengths[F6CODE],&magbuf[magidx]); magidx+=codesize; // the string1 is stored in F9 memcpy(&magbuf[magidx],&dskimg[offsets[F9STRING1]],lengths[F9STRING1]); string1size=lengths[F9STRING1]; magidx+=string1size; // string2 is in F8, it is packed string2size=loader_common_unhuffer(&dskimg[offsets[F8STRING2]],lengths[F8STRING2],&magbuf[magidx]); magidx+=string2size; if (loader_archimedes_cGames[gameId].version) // the pawn did not have a dictionary file. { // the dict is stored in F7, it is packed dictsize=loader_common_unhuffer(&dskimg[offsets[F7DICT]],lengths[F7DICT],&magbuf[magidx]); magidx+=dictsize; } else { dictsize=0; } loader_common_addmagheader(magbuf,magidx,loader_archimedes_cGames[gameId].version,codesize,string1size,string2size,dictsize,-1); *magsize=magidx; return LOADER_OK; } // the archimedes basically uses the same graphic format as the Amiga and the Atari. // all that is needed is to find the offsets to the pictures. int loader_archimedes_mkgfx(unsigned char *dskimg,int dsksize,unsigned char* gfxbuf,int* gfxsize, int gameId,int* offsets,int *lengths) { int gfxcnt; int idx; // TODO: since the memory is shared, make sure that offsets[10]>260!!! gfxbuf[0]='M';gfxbuf[1]='a';gfxbuf[2]='P';gfxbuf[3]='i'; WRITE_INT32BE(gfxbuf,4,*gfxsize); idx=offsets[F10PICTURES]; gfxcnt=0; while (idx<(offsets[F10PICTURES]+lengths[F10PICTURES]-48)) { // i do not know where the index for the pictures is. // their format is as followed: there is a 48 byte header. then comes the picture data. // luckily, within this header, at position 0x10, there is a magic sequece 00 00 07 77. // that is sufficient to find the end of the header, and consequently, the beginning of the picture data. if (dskimg[idx+0x10]==0x00 && dskimg[idx+0x11]==0x00 && dskimg[idx+0x12]==0x07 && dskimg[idx+0x13]==0x77) { WRITE_INT32BE(gfxbuf,4*loader_archimedes_cGames[gameId].pictureorder[gfxcnt]+8,idx+48); gfxcnt++; } idx++; } return LOADER_OK; } int loader_archimedes(char* filename,char* magbuf,int* magsize,char* gfxbuf,int* gfxsize) { FILE *f; int newgfxsize; int offsets[MAXFILENAMENUM+1]={0}; int lengths[MAXFILENAMENUM+1]={0}; int gameId=-1; // read the input file. // actually, use the gfx buffer for that one. the data will be read linearly, the // pictures are already in the GFX1 format. all that is needed is a header and // an index. newgfxsize=*gfxsize; f=fopen(filename,"rb"); newgfxsize=fread(gfxbuf,sizeof(char),newgfxsize,f); fclose(f); if (loader_archimedes_findfiles((unsigned char*)gfxbuf,newgfxsize,&gameId,offsets,lengths)!=LOADER_OK) { return LOADER_NOK; } // since the data is in the gfx buf, we need to generate the magbuf first. if (loader_archimedes_mkmag((unsigned char*)gfxbuf,newgfxsize,(unsigned char*)magbuf,magsize, gameId,offsets,lengths)!=LOADER_OK) { return LOADER_NOK; } // now all the relevant data from the gfxbuf has been read, it is okay to overwrite it. if (loader_archimedes_mkgfx((unsigned char*)gfxbuf,newgfxsize,(unsigned char*)gfxbuf,gfxsize, gameId,offsets,lengths)!=LOADER_OK) { return LOADER_NOK; } *gfxsize=newgfxsize; return LOADER_OK; } dmagnetic-0.32/src/loader/loader_dsk.h0000644000175000017500000000270014076357617017327 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LOADER_DSK_H #define LOADER_DSK_H int loader_dsk(char* amstradcpcname, char *magbuf,int* magsize, char* gfxbuf,int* gfxsize, int amstrad0spectrum1); #endif dmagnetic-0.32/src/loader/loader_mw.c0000644000175000017500000002730514076357617017174 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // the purpose of this file is to read the resource files from the // Magnetic Windows releases, and transform them into the .mag/.gfx format. #include #include #include #include "loader_common.h" #include "loader_mw.h" #include "configuration.h" #include "vm68k_macros.h" // the purpose of this function is to use the naming of the "???one.rsc" file as a blueprint. int loader_mw_substituteTwoRsc(char* two_rsc,int num,char* output) { int i; int l; int onestart; int uppercase; char *names[12]={"zero","one","two","three","four","five","six","seven","eight","nine","title.vga","title.ega"}; l=strlen(two_rsc); onestart=-1; uppercase=0; for (i=0;i=12 || num<0) return 0; memcpy(output,two_rsc,strlen(two_rsc)); memcpy(&output[onestart],&names[num][0],strlen(names[num])+1); if (uppercase) { for (i=0;i0x10000) text2size=text1size+dictsize-0x10000; else text2size=text1size+dictsize-0xe000; loader_common_addmagheader((unsigned char*)magbuf,magidx,4,codesize,(text1size>=0x10000)?0x10000:0xe000,text2size,wtabsize,text1size); *bytes=magidx; return 1; } int loader_magneticwindows(char* two_rsc, char *magbuf,int* magsize, char* gfxbuf,int* gfxsize) { int bytes; int sizes[10]={0}; printf("Pondering...\n"); if (!loader_mw_collectSizes(two_rsc,sizes,gfxbuf)) { fprintf(stderr,"unable to find resource files\n"); fprintf(stderr," please make sure that they are named TWO.RSC\n"); fprintf(stderr," CTWO.RSC or something similar.\n"); return 1; } if (loader_mw_mkmag(two_rsc,sizes,magbuf,&bytes)) { *magsize=bytes; loader_mw_mkgfx(two_rsc,sizes,gfxbuf,gfxsize); } else { *magsize=0; *gfxsize=0; } return 0; } dmagnetic-0.32/src/loader/loader_d64.h0000644000175000017500000000264014076357617017146 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LOADER_D64_H #define LOADER_D64_H int loader_d64(char* d64name, char *magbuf,int* magsize, char* gfxbuf,int* gfxsize); #endif dmagnetic-0.32/src/gui/0000755000175000017500000000000014076357622014362 5ustar dettusdettusdmagnetic-0.32/src/gui/default_palette.c0000644000175000017500000001653014076357617017701 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "default_palette.h" #include "picture.h" /// this lookuptable has the following structure: /// 4 bits substitute color, 3x4 bit RGB values. /// this is a direct 1 <--> 1 conversion. const unsigned short rgblut[]={ 0x0000,0x1400,0x3420,0x4004,0x5404,0x6044,0x7444,0x8222,0x9722,0xa272,0xb772,0xc227,0xd727,0xe277,0xf777,0x2121, 0xb554,0x9654,0x6454,0xa452,0x4223,0x7455,0x2342,0x8333,0x1322,0x7443,0x2230,0xc456,0xe565,0x7555,0xb653,0x0110, 0x4334,0xf666,0xe665,0x3553,0x1210,0x3432,0x2332,0xc444,0xa665,0x7332,0xb665,0xf775,0xb442,0x2232,0x6122,0xc223, 0x8111,0x5212,0x4112,0xb631,0x3310,0x8211,0x1410,0x9520,0x2221,0x0100,0xb742,0xb740,0x9731,0x9620,0xa351,0xe454, 0xb460,0x6343,0x2243,0xc354,0x0121,0xb443,0x3331,0x3332,0xf565,0x2341,0x8221,0x3321,0xa040,0xa450,0xb550,0x9763, 0x1100,0x0111,0xb664,0x9754,0xb753,0x3642,0x1211,0x5433,0xc556,0xb552,0x3441,0x0011,0xe566,0xe344,0xf677,0x6233, 0x4222,0x3221,0xe676,0xc445,0x0222,0x3543,0x0010,0x2120,0x5101,0xc667,0xb663,0x1311,0x9533,0x0001,0x6455,0x9655, 0x1200,0x3110,0x4001,0x9543,0xd544,0x6011,0x9652,0xb764,0xc123,0xe234,0xc224,0x1542,0x6022,0x5323,0xc555,0x9432, 0x3431,0x8110,0x9422,0xb520,0xc567,0xa352,0xe466,0x6344,0x7333,0x9433,0x2030,0x1510,0x2231,0xd704,0x2020,0xc005, 0x9544,0x8433,0xa453,0x9322,0x3532,0x7665,0xa565,0x9733,0xf776,0xb765,0x4445,0x1432,0xb774,0x5322,0xa573,0xe563, 0x3210,0xa673,0x9700,0xf675,0xf665,0xf555,0xf554,0xc334,0xe675,0xd766,0xa564,0x9775,0x7554,0x3440,0x8554,0x0333, 0x0332,0xc335,0x1433,0xd655,0xd645,0x7544,0x1644,0xd755,0xb775,0x3443,0xc345,0xa342,0x8443,0xc236,0x1623,0x9743, 0xb762,0x2151,0x5507,0xd606,0xd767,0xa675,0x8122,0xa343,0x0245,0x1402,0x4024,0xc245,0x6133,0x5313,0x5202,0xb630, 0x4012,0xe345,0xc234,0xa332,0x3320 }; #define RGBLUTSIZE 213 // another strategy would be to calculate the center of the clusters that make up the 16 colors. const unsigned int rgbcenters[16]={0x0aa36cc2,0x217380c2,0x0f77049d,0x20f678b2,0x0c23cdb6,0x1b6249b6,0x107789c5,0x2799e638,0x16d57924,0x3487fd74,0x236c457f,0x366ba166,0x1be82eda,0x3ad81f1b,0x26dcbeda,0x36ce6323}; int default_findrgbcluster(int red,int green,int blue) { int i; int closest=0; int mindelta=-1; int delta; int r0,r1,g0,g1,b0,b1; r0=red; g0=green; b0=blue; #if 0 // if I ever want to calculate better rgb centers. { int rgbsums[16][3]={{0}}; int colcnt[16]={0}; for (i=0;i>12)&0xf; red =(rgblut[i]>> 8)&0xf; green =(rgblut[i]>> 4)&0xf; blue =(rgblut[i]>> 0)&0xf; rgbsums[col][0]+=red; rgbsums[col][1]+=green; rgbsums[col][2]+=blue; colcnt[col]+=7; } printf("const unsigned int rgbcenters[16]={"); for (i=0;i<16;i++) { rgbsums[i][0]*=PICTURE_MAX_RGB_VALUE; rgbsums[i][1]*=PICTURE_MAX_RGB_VALUE; rgbsums[i][2]*=PICTURE_MAX_RGB_VALUE; rgbsums[i][0]/=(colcnt[i]); rgbsums[i][1]/=(colcnt[i]); rgbsums[i][2]/=(colcnt[i]); rgbcenters[i]=(rgbsums[i][0]<<20)|(rgbsums[i][1]<<10)|(rgbsums[i][2]); printf("0x%08x,",rgbcenters[i]); } printf("};\n"); } #endif for (i=0;i<16;i++) { r1=(rgbcenters[i]>>20)&0x3ff; g1=(rgbcenters[i]>>10)&0x3ff; b1=(rgbcenters[i]>> 0)&0x3ff; delta =(r0-r1)*(r0-r1); delta+=(g0-g1)*(g0-g1); delta+=(b0-b1)*(b0-b1); if (delta>15)&1)+ (((x)>>14)&1)+ (((x)>>13)&1)+ (((x)>>12)&1)+ \ (((x)>>11)&1)+ (((x)>>10)&1)+ (((x)>> 9)&1)+ (((x)>> 8)&1)+ \ (((x)>> 7)&1)+ (((x)>> 6)&1)+ (((x)>> 5)&1)+ (((x)>> 4)&1)+ \ (((x)>> 3)&1)+ (((x)>> 2)&1)+ (((x)>> 1)&1)+ (((x)>> 0)&1)) // each rgb from the pictures has been assigned one or more alternative // ANSI colors. this method is searching for a combination of alternatives // that is most diverse. total=1; // step 1: collect the alternatives for (i=0;i<16;i++) { unsigned int rgb; rgb=picture->palette[i]; { int red,green,blue; red= PICTURE_GET_RED(rgb); green= PICTURE_GET_GREEN(rgb); blue= PICTURE_GET_BLUE(rgb); red*=7;green*=7;blue*=7; red+=(PICTURE_MAX_RGB_VALUE/2); green+=(PICTURE_MAX_RGB_VALUE/2); blue+=(PICTURE_MAX_RGB_VALUE/2); red/=PICTURE_MAX_RGB_VALUE;green/=PICTURE_MAX_RGB_VALUE;blue/=PICTURE_MAX_RGB_VALUE; rgb=(red<<8)|(green<<4)|blue; } numcols[i]=0; for (j=0;j>12)&0xf; } } // plan B: if there has been no direct match, make sure there is at least one alternative if (numcols[i]==0) { ansicols[i][numcols[i]++]=default_findrgbcluster(PICTURE_GET_RED(picture->palette[i]),PICTURE_GET_GREEN(picture->palette[i]),PICTURE_GET_BLUE(picture->palette[i])); } total*=numcols[i]; if (total>=16777216) total=16777216; // arbitrary upper limit. i do not want to do the "pondering..." for too long. } maxcols=0; maxi=0; // step 2: go through all the possible combinations and see which one is the most diverse. for (i=0;imaxcols) // more colors in this combination-> more bits in the mask -> more diversity in the final picture. { maxcols=bitcnt; maxi=i; // best combination so far. } } // at this point, maxi is the most diverse combination. // step 3: map it to the output palette for (i=0;i<16;i++) { maxplut[i]=ansicols[i][maxi%numcols[i]]; maxi/=numcols[i]; } return 0; } dmagnetic-0.32/src/gui/default_palette.h0000644000175000017500000000317214076357617017704 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef DEFAULT_PALETTE_H #define DEFAULT_PALETTE_H #include "picture.h" // one strategy could be to find the center of 16 clusters that could be interpreted as "red", for example int default_findrgbcluster(int red,int green,int blue); // the other involves heavy experimentation. int default_palette(tPicture* picture,unsigned char* maxplut); #endif dmagnetic-0.32/src/gui/default_callbacks.c0000644000175000017500000007637314076357617020175 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // the purpose of this file is to provide a callback-fallback until proper // user interfaces have been established. It will do ;) #include #include #include #include "default_render.h" #include "default_palette.h" #include "picture.h" #include "configuration.h" #include "default_callbacks.h" #define MAGIC 0x68654879 // = yHeh, the place where I grew up ;) #define MAXTEXTBUFFER 1024 // maximum number of buffered characters #define MAXHEADLINEBUFFER 256 // maximum number of buffered headline characters #define MAX_SRESX 4096 typedef enum _tMode { eMODE_NONE, eMODE_MONOCHROME, eMODE_LOW_ANSI, eMODE_LOW_ANSI2, eMODE_HIGH_ANSI, eMODE_HIGH_ANSI2, eMODE_SIXEL, eMODE_UTF } tMode; typedef struct _tContext { unsigned int magic; // ansi output int columns; int rows; tMode mode; // 0=none. 1=ascii art. 2=ansi art. 3=high ansi. 4=high ansi2 // log the input FILE* f_logfile; int echomode; int capital; // =1 if the next character should be a capital letter. int lastchar; int jinxterslide; // THIS workaround makes the sliding puzzle in Jinxter solvable int headlineflagged; char low_ansi_characters[128]; // characters that are allowed for low ansi rendering char monochrome_characters[128]; // characters that are allowed for monochrome rendering int monochrome_inverted; // this is the line buffer. // text is stored here, before // being printed on the console. int textalign; // 0=left aligned. 1=block aligned int textlastspace; int textidx; char textoutput[MAXTEXTBUFFER]; int headlineidx; char headlineoutput[MAXHEADLINEBUFFER]; // sixel parameter int screenheight; int screenwidth; int forceres; } tContext; int default_flushOutputLeftAlign(tContext* pContext,int newline) { if (newline || pContext->textlastspace==-1) { pContext->textoutput[pContext->textidx++]=0; printf("%s",pContext->textoutput); pContext->textidx=0; } else { int i; int j; pContext->textoutput[pContext->textlastspace]=0; printf("%s\n",pContext->textoutput); j=0; for (i=pContext->textlastspace+1;itextidx;i++) { pContext->textoutput[j++]=pContext->textoutput[i]; } pContext->textidx=j; } pContext->textlastspace=-1; return 0; } int default_flushOutputRightAlign(tContext* pContext,int newline) { int i; if (newline || pContext->textlastspace==-1) { for (i=0;icolumns-pContext->textidx;i++) printf(" "); pContext->textoutput[pContext->textidx++]=0; printf("%s",pContext->textoutput); pContext->textidx=0; } else { int j; for (i=0;icolumns-pContext->textlastspace;i++) printf(" "); pContext->textoutput[pContext->textlastspace]=0; printf("%s\n",pContext->textoutput); j=0; for (i=pContext->textlastspace+1;itextidx;i++) { pContext->textoutput[j++]=pContext->textoutput[i]; } pContext->textidx=j; } pContext->textlastspace=-1; return 0; } int default_flushOutputBlockAlign(tContext* pContext,int newline) { if (newline || pContext->textlastspace==-1) { pContext->textoutput[pContext->textidx++]=0; printf("%s",pContext->textoutput); pContext->textidx=0; } else { int i; int j; int spacecnt; int rightmargin; int accu; spacecnt=0; rightmargin=pContext->columns-pContext->textlastspace; pContext->textoutput[pContext->textlastspace]=0; for (i=0;itextlastspace;i++) { if (pContext->textoutput[i]==' ') spacecnt++; } accu=0; for (i=0;itextlastspace;i++) { if (pContext->textoutput[i]!=' ') printf("%c",pContext->textoutput[i]); else { printf(" "); accu+=rightmargin; { while (accu>=spacecnt) { printf(" "); accu-=spacecnt; } } } } printf("\n"); j=0; for (i=pContext->textlastspace+1;itextidx;i++) { pContext->textoutput[j++]=pContext->textoutput[i]; } pContext->textidx=j; } pContext->textlastspace=-1; return 0; } int default_flushOutput(tContext* pContext,int newline) { switch(pContext->textalign) { case 1: return default_flushOutputBlockAlign(pContext,newline); case 2: return default_flushOutputRightAlign(pContext,newline); case 0: default: return default_flushOutputLeftAlign(pContext,newline); break; } return 0; } int default_cbOutputChar(void* context,char c,unsigned char controlD2,unsigned char flag_headline) { tContext *pContext=(tContext*)context; int newline; unsigned char c2; if (flag_headline && !pContext->headlineflagged) { pContext->headlineidx=0; } if (!flag_headline && pContext->headlineflagged) // after the headline ends, a new paragraph is beginning. { int i; pContext->capital=1; // obviously, this starts with a captial letter. pContext->headlineoutput[pContext->headlineidx]=0; // highlight the headline //printf("\x1b[0;30;47m%s\x1b[0m\n",pContext->headlineoutput); // first: remove the newlines from the headline text. for (i=0;iheadlineidx;i++) { if (pContext->headlineoutput[i]==0x0a) { pContext->headlineoutput[i]=0; pContext->headlineidx--; } } // second, print the ----[HEADLINE]- if (pContext->mode==eMODE_NONE) { printf("\n[%s]\n",pContext->headlineoutput); } else { for (i=0;icolumns-pContext->headlineidx-3;i++) { printf("-"); } if (pContext->mode==eMODE_LOW_ANSI || pContext->mode==eMODE_HIGH_ANSI) // high or low ansi -> make the headline text pop out printf("[\x1b[0;30;47m%s\x1b[0m]-\n",pContext->headlineoutput); else printf("[%s]-\n",pContext->headlineoutput); } } pContext->headlineflagged=flag_headline; newline=0; if ((unsigned char)c==0xff) { pContext->capital=1; } else { c2=c&0x7f; // the highest bit was an end marker for the hufman tree in the dictionary. // THE RULES FOR THE OUTPUT ARE: // replace tabs and _ with space. // the headline is printed in upper case letters. // after a . there has to be a space. // and after a . The next letter has to be uppercase. // multiple spaces are to be reduced to a single one. // the characters ~ and ^ are to be translated into line feeds. // the caracter 0xff makes the next one upper case. // after a second newline comes a capital letter. if (c2==9 || c2=='_') c2=' '; if (flag_headline && (c2==0x5f || c2==0x40)) c2=' '; // in a headline, those are the control codes for a space. if (controlD2 && c2==0x40) return 0; // end marker if (c2==0x5e || c2==0x7e) c2=0x0a; // ~ or ^ is actually a line feed. if (c2==0x0a && pContext->lastchar==0x0a) // after two consequitive newlines comes a capital letter. { pContext->capital=1; } if (c2=='.' || c2=='!' || c2==':' || c2=='?') // a sentence is ending. { pContext->capital=1; } if (((c2>='a' && c2<='z') || (c2>='A' && c2<='Z')) && (pContext->capital||flag_headline)) { pContext->capital=0; c2&=0x5f; // upper case } newline=0; if ( (pContext->lastchar=='.' || pContext->lastchar=='!' || pContext->lastchar==':' || pContext->lastchar=='?'|| pContext->lastchar==',' || pContext->lastchar==';') // a sentence as ended && ((c2>='A' && c2<='Z') ||(c2>='a' && c2<='z') ||(c2>='0' && c2<='9'))) // and a new one is beginning. { if (flag_headline) { if (pContext->headlineidxheadlineoutput[pContext->headlineidx++]=' '; // after those letters comes an extra space. } else { if (pContext->textidxtextlastspace=pContext->textidx; pContext->textoutput[pContext->textidx++]=' '; } } //pContext->lastchar=' '; } if (pContext->textidx>0 && pContext->lastchar==' ' && (c2==',' || c2==';' || c2=='.' || c2=='!')) // there have been some glitches with extra spaces, right before a komma. which , as you can see , looks weird. { pContext->textidx--; } if ( //allow multiple spaces in certain scenarios flag_headline || pContext->jinxterslide || pContext->lastchar!=' ' || c2!=' ') // combine multiple spaces into a single one. { if (c2==0x0a || (c2>=32 && c2<127 && c2!='@')) { if (flag_headline) { if (pContext->headlineidxheadlineoutput[pContext->headlineidx++]=c2&0x7f; } else if (pContext->textidx='0' && c2<='9')) pContext->jinxterslide=1; // a workaround regarding the sliding puzzle in jinxter. else if (c2!=' ') pContext->jinxterslide=0; // sometimes multiple spaces are bad, but in this case it is not. if (c2==' ') pContext->textlastspace=pContext->textidx; pContext->textoutput[pContext->textidx++]=c2; if (c2=='\n') newline=1; } pContext->lastchar=c2; } } if (newline || pContext->textidx>=pContext->columns || pContext->textidx>=511) { default_flushOutput(pContext,newline); } } return 0; } int default_cbOutputString(void* context,char* string,unsigned char controlD2,unsigned char flag_headline) { unsigned char c2; int i; i=0; c2=0; do { c2=string[i++]&0x7f; default_cbOutputChar(context,c2,controlD2,flag_headline); } while (c2>=32); return 0; } int default_cbInputString(void* context,int* len,char* string) { int l; tContext *pContext=(tContext*)context; default_flushOutput(pContext,1); fflush(stdout); if (feof(stdin)) exit(0); if (fgets(string,256,stdin)==NULL) exit(0); if (pContext->echomode) { for (l=0;l='a' && string[l]<='z') { printf("%c",string[l]&0x5f); } else { printf("%c",string[l]); // print upper case letters. } } } if (pContext->f_logfile) { fprintf(pContext->f_logfile,"%s",string); fflush(pContext->f_logfile); } l=strlen(string); *len=l; pContext->capital=1; // the next line after the input is most definiately the beginning of a sentence! return 0; } int default_cbDrawPicture(void* context,tPicture* picture,int mode) { int i; int j; int rgb; int accux,accuy; tContext *pContext=(tContext*)context; int lastrgb; if (pContext->mode==eMODE_NONE) return 0; // flush the output buffer default_cbOutputChar(context,'\n',0,0); if (pContext->mode==eMODE_MONOCHROME) { default_render_monochrome(pContext->monochrome_characters,pContext->monochrome_inverted,picture,pContext->rows,pContext->columns); } if (pContext->mode==eMODE_LOW_ANSI) { default_render_lowansi(pContext->low_ansi_characters,picture,pContext->rows,pContext->columns); } if (pContext->mode==eMODE_LOW_ANSI2) { default_render_lowansi2(pContext->low_ansi_characters,picture,pContext->rows,pContext->columns); } if (pContext->mode==eMODE_HIGH_ANSI2) { int redsum,greensum,bluesum; int lastx; int lasty; int pixcnt; redsum=0; greensum=0; bluesum=0; lastx=lasty=0; pixcnt=0; accux=accuy=0; for (i=0;iheight;i++) { accuy+=pContext->rows; lastrgb=-1; if (accuy>=picture->height || i==picture->height-1) { accuy-=picture->height; accux=0; lastx=0; for (j=0;jwidth;j++) { accux+=pContext->columns; if (accux>=picture->width || j==picture->width-1) { int x,y; redsum=0; greensum=0; bluesum=0; accux-=picture->width; pixcnt=0; for (y=lasty;y<=i;y++) { for (x=lastx;x<=j;x++) { unsigned int p; p=picture->palette[(int)(picture->pixels[y*(picture->width)+x])]; redsum +=(PICTURE_GET_RED(p)); greensum+=(PICTURE_GET_GREEN(p)); bluesum +=(PICTURE_GET_BLUE(p)); pixcnt++; } } redsum*=255;greensum*=255;bluesum*=255; pixcnt*=PICTURE_MAX_RGB_VALUE; redsum/=pixcnt;greensum/=pixcnt;bluesum/=pixcnt; if (redsum>255) redsum=255; if (greensum>255) greensum=255; if (bluesum>255) bluesum=255; rgb=(redsum<<16)|(greensum<<8)|bluesum; if (rgb!=lastrgb) { printf("\x1b[48;2;%d;%d;%dm", redsum,greensum,bluesum); lastrgb=rgb; } printf(" "); lastx=j; } } printf("\x1b[0m\n"); lasty=i; } } } if (pContext->mode==eMODE_HIGH_ANSI) { accux=accuy=0; for (i=0;iheight;i++) { accuy+=pContext->rows; lastrgb=-1; if (accuy>=picture->height || i==picture->height-1) { accux=0; for (j=0;jwidth;j++) { accux+=pContext->columns; if (accux>=picture->width || j==picture->width-1) { rgb=picture->palette[(int)(picture->pixels[i*(picture->width)+j])]; if (rgb!=lastrgb) { int red,green,blue; red =PICTURE_GET_RED(rgb); green=PICTURE_GET_GREEN(rgb); blue =PICTURE_GET_BLUE(rgb); red*=255;green*=255;blue*=255; red /=PICTURE_MAX_RGB_VALUE; green/=PICTURE_MAX_RGB_VALUE; blue /=PICTURE_MAX_RGB_VALUE; printf("\x1b[48;2;%d;%d;%dm", red,green,blue); } printf(" "); lastrgb=rgb; accux-=picture->width; } } accuy-=picture->height; printf("\x1b[0m\n"); } } } if (pContext->mode==eMODE_SIXEL) // sixels { // so, according to James, some terminals have only 16 registers for the colours. // thus, the colour for text foreground and the background might be overwritten, // when too many sixel colours are being defined. // // the solution is to make sure that the sixth entry contains the brightest // and the 15th entry the darkest colour. So I am reordering the palette. #define FOREGROUND_POSITION 6 #define BACKGROUND_POSITION 15 #define TOTAL_COLOURS 16 int i,j; int accux,accuy; int x,y; int screenheight; int screenwidth; int forceres; int minval=0; int minpos=0; int maxval=0; int maxpos=0; int paletteorder[TOTAL_COLOURS]; x=0;y=0; accux=accuy=0; screenheight=pContext->screenheight; screenwidth=pContext->screenwidth; forceres=pContext->forceres; // find a good aspect ratio { int ratiox,ratioy; #define MAGICFIXPOINT 1800 // magic factor was chosen when I removed the float code. simply because it worked with the check and it did not cause overflows with 32 bit machines if (picture->width<=0 || picture->height<=1) return 0; // make sure that there will be no division by 0 ratiox=(MAGICFIXPOINT*screenwidth)/(picture->width); ratioy=(MAGICFIXPOINT*screenheight)/(picture->height-1); if (!forceres) { if (ratioxwidth)/MAGICFIXPOINT); screenheight=(int)((ratioy*(picture->height-1))/MAGICFIXPOINT); if (!forceres) { while (screenheight%6) screenheight++; // make sure that the last line consists of a full block of sixels. } } // find the darkest and the brightest color. // move them to pallette position 0 or 15, to deal with an issue that james found { int j; for (i=0;ipalette[i]; red =PICTURE_GET_RED(rgb); green=PICTURE_GET_GREEN(rgb); blue =PICTURE_GET_BLUE(rgb); val=red+green+blue; // just add the values. luminance correction would be overkill if (i==0 || minval>val) { minval=val; minpos=i; } if (i==0 || maxvalpalette[paletteorder[i]]; red =PICTURE_GET_RED(rgb); green=PICTURE_GET_GREEN(rgb); blue =PICTURE_GET_BLUE(rgb); red*=100;green*=100;blue*=100; red /=PICTURE_MAX_RGB_VALUE; green/=PICTURE_MAX_RGB_VALUE; blue /=PICTURE_MAX_RGB_VALUE; printf("#%02d;2;%d;%d;%d",paletteorder[i],red,green,blue); } while (y<(picture->height-1)) { int y0; int accuy0; int curpixel; accuy0=accuy; y0=y; for (i=0;iwidth) { char bitmask; y=y0; accuy=accuy0; curpixel=picture->pixels[y*picture->width+x]; bitmask=0; for (j=0;j<6;j++) { bitmask>>=1; if (curpixel==i) bitmask|=0x20; accuy+=(picture->height-1); while (accuy>=screenheight) { accuy-=screenheight; y++; if (y<(picture->height-1)) { curpixel=picture->pixels[y*picture->width+x]; } else { curpixel=0; } } } while (accuxwidth; } accux-=screenwidth; x++; } printf("$"); } printf("-"); } printf("\x1b\\\x1b[0m\n"); } if (pContext->mode==eMODE_UTF) // utf { // By choosing the following utf-characters: // 0x20,0xe29680,0xe29690,0xe2969a,0xe29696,0xe29697,0xe29698,0xe2969d // // i could transform each character field into a 2x2 bitmap: // .. ## .# #. .. .. #. .# // .. .. .# .# #. .# .. .. // // when choosing the background and the foreground colour wisely, it // would create the illusion of finer grained pixels, even though, // technically, it is not exact. It would work best when each 2x2 bitmap // has only 1 or 2 colours. But with 3 or 4 it creates artifacts. // they are negligable! // // it can easily be seen that those 8 utf symbols are enough #define NUM_utf8chars 8 #define CALC_RGBDELTA(rgb1,rgb2) ( \ (PICTURE_GET_RED(rgb1)-PICTURE_GET_RED(rgb2))*(PICTURE_GET_RED(rgb1)-PICTURE_GET_RED(rgb2))+ \ (PICTURE_GET_GREEN(rgb1)-PICTURE_GET_GREEN(rgb2))*(PICTURE_GET_GREEN(rgb1)-PICTURE_GET_GREEN(rgb2))+ \ (PICTURE_GET_BLUE(rgb1)-PICTURE_GET_BLUE(rgb2))*(PICTURE_GET_BLUE(rgb1)-PICTURE_GET_BLUE(rgb2))) #define NUM_COLOURS 16 // for each of the utf characters, define a bitmap according to the following scheme: // 12 // 48 const unsigned int default_cbDrawPicture_utf8chars[NUM_utf8chars]={0x200000,0xe29680,0xe29690,0xe2969a,0xe29696,0xe29697,0xe29698,0xe2969d}; const unsigned char default_cbDrawPicture_utf8map[NUM_utf8chars] ={0x0, 1|2, 2|8, 1|8, 4, 8, 1, 2}; int x; int y; int accux; int accuy; accuy=0; for (y=0;yheight;y++) { accuy+=pContext->rows; if (accuy>=picture->height) { accuy-=picture->height; accux=0; for (x=0;xwidth;x++) { unsigned int lastfg,lastbg; lastfg=lastbg=0xffffffff; accux+=pContext->columns; if (accux>=picture->width) { unsigned int rgb1,rgb2,rgb4,rgb8; unsigned int mindelta; unsigned int bestutfsymbol; unsigned int bestfg; unsigned int bestbg; int fg,bg,sym; int fgred,fggreen,fgblue; int bgred,bggreen,bgblue; accux-=picture->width; // find out what colours are in the 2x2 bitmap rgb1=rgb2=rgb4=rgb8=picture->palette[(int)(picture->pixels[(y+0)*(picture->width)+x+0])]; if (xwidth-1) rgb2=picture->palette[(int)(picture->pixels[(y+0)*(picture->width)+x+1])]; if (yheight-1) rgb4=rgb8=picture->palette[(int)(picture->pixels[(y+1)*(picture->width)+x+0])]; if (xwidth-1 && yheight-1) rgb8=picture->palette[(int)(picture->pixels[(y+1)*(picture->width)+x+1])]; // now for the main event: try to find a matching foreground/background/symbol combination // using a neighest neighbour-approach with the RGB values helps in nuanced picture to // mask some artifacts mindelta=bestfg=bestbg=0xffffffff; bestutfsymbol=0; for (fg=0;fgpalette[fg]:picture->palette[bg]); delta+=CALC_RGBDELTA(rgb2,(default_cbDrawPicture_utf8map[sym]&2)?picture->palette[fg]:picture->palette[bg]); delta+=CALC_RGBDELTA(rgb4,(default_cbDrawPicture_utf8map[sym]&4)?picture->palette[fg]:picture->palette[bg]); delta+=CALC_RGBDELTA(rgb8,(default_cbDrawPicture_utf8map[sym]&8)?picture->palette[fg]:picture->palette[bg]); if (deltapalette[fg]; bestbg=picture->palette[bg]; bestutfsymbol=default_cbDrawPicture_utf8chars[sym]; } } } } // now the best fg/bg/sym combination is known. let's render it! // first: the colour if (lastfg!=bestfg) { lastfg=bestfg; fgred =PICTURE_GET_RED(bestfg); fggreen=PICTURE_GET_GREEN(bestfg); fgblue =PICTURE_GET_BLUE(bestfg); fgred*=255;fggreen*=255;fgblue*=255; fgred /=PICTURE_MAX_RGB_VALUE; fggreen/=PICTURE_MAX_RGB_VALUE; fgblue /=PICTURE_MAX_RGB_VALUE; printf("\x1b[38;2;%d;%d;%dm",fgred,fggreen,fgblue); } if (lastbg!=bestbg) { lastbg=bestbg; bgred =PICTURE_GET_RED(bestbg); bggreen=PICTURE_GET_GREEN(bestbg); bgblue =PICTURE_GET_BLUE(bestbg); bgred*=255;bggreen*=255;bgblue*=255; bgred /=PICTURE_MAX_RGB_VALUE; bggreen/=PICTURE_MAX_RGB_VALUE; bgblue /=PICTURE_MAX_RGB_VALUE; printf("\x1b[48;2;%d;%d;%dm",bgred,bggreen,bgblue); } // since the uft8symbols above are defined as BIG endian numbers, and do not have a 0x00 at the end, // they can be displayed one byte at a time. MSB first. while (bestutfsymbol) { printf("%c",(bestutfsymbol>>16)&0xff); bestutfsymbol<<=8; bestutfsymbol&=0x00ffff00; } } } // that was one line printf("\x1b[0m\n"); } } } return 0; } int default_cbSaveGame(void* context,char* filename,void* ptr,int len) { FILE *f; int n; f=fopen(filename,"wb"); if (!f) { printf("Unable to open file [%s]\n",(char*)ptr); return 0; } n=fwrite(ptr,sizeof(char),len,f); fclose(f); if (n==len) return 0; return -1; } int default_cbLoadGame(void* context,char* filename,void* ptr,int len) { FILE *f; int n; f=fopen(filename,"rb"); if (!f) { printf("Unable to open file [%s]\n",(char*)ptr); return 0; } n=fread(ptr,sizeof(char),len,f); fclose(f); if (n==len) return 0; return -1; } int default_getsize(int* size) { if (size==NULL) return DEFAULT_NOK; *size=sizeof(tContext); return DEFAULT_OK; } int default_open(void* hContext,FILE *f_inifile,int argc,char** argv) { tContext *pContext=(tContext*)hContext; char result[1024]; #define DEFAULT_LOW_ANSI_CHARACTERS 8 const char default_low_ansi_characters[DEFAULT_LOW_ANSI_CHARACTERS]="\\/|=L#T"; #define DEFAULT_MONOCHROME_CHARACTERS 14 const char default_monochrome_characters[DEFAULT_MONOCHROME_CHARACTERS]=" .-=*"; if (pContext==NULL) return DEFAULT_NOK; memset(pContext,0,sizeof(tContext)); pContext->magic=MAGIC; // the hiearchy is: first the default values. // if there is a .ini file, overwrite them with those values. // paramaters from the command line have the highest prioprity. pContext->rows=40; pContext->columns=120; pContext->mode=eMODE_LOW_ANSI; // 0=none. 1=monochrome. 2=low_ansi. 3=high_ansi. 4=high_ansi2, 5=sixel pContext->f_logfile=NULL; pContext->echomode=0; pContext->textalign=1; pContext->textidx=0; pContext->textlastspace=-1; pContext->screenwidth=320; pContext->screenheight=200; pContext->forceres=0; memcpy(pContext->low_ansi_characters,default_low_ansi_characters,sizeof(default_low_ansi_characters)); memcpy(pContext->monochrome_characters,default_monochrome_characters,sizeof(default_monochrome_characters)); if (f_inifile) { if (retrievefromini(f_inifile,"[DEFAULTGUI]","rows",result,sizeof(result))) { pContext->rows=atoi(result); } if (retrievefromini(f_inifile,"[DEFAULTGUI]","columns",result,sizeof(result))) { pContext->columns=atoi(result); } if (retrievefromini(f_inifile,"[DEFAULTGUI]","mode",result,sizeof(result))) { if (strncmp(result,"none",4)==0) pContext->mode=eMODE_NONE; else if (strncmp(result,"monochrome_inv",14)==0) {pContext->mode=eMODE_MONOCHROME;pContext->monochrome_inverted=1;} else if (strncmp(result,"monochrome",10)==0) pContext->mode=eMODE_MONOCHROME; else if (strncmp(result,"low_ansi2",9)==0) pContext->mode=eMODE_LOW_ANSI2; else if (strncmp(result,"low_ansi",8)==0) pContext->mode=eMODE_LOW_ANSI; else if (strncmp(result,"high_ansi2",10)==0) pContext->mode=eMODE_HIGH_ANSI2; else if (strncmp(result,"high_ansi",9)==0) pContext->mode=eMODE_HIGH_ANSI; else if (strncmp(result,"sixel",5)==0) pContext->mode=eMODE_SIXEL; else if (strncmp(result,"utf",4)==0) pContext->mode=eMODE_UTF; } if (retrievefromini(f_inifile,"[DEFAULTGUI]","align",result,sizeof(result))) { if (strncmp(result,"left",4)==0) pContext->textalign=0; if (strncmp(result,"block",5)==0) pContext->textalign=1; if (strncmp(result,"right",5)==0) pContext->textalign=2; } if (retrievefromini(f_inifile,"[DEFAULTGUI]","low_ansi_characters",pContext->low_ansi_characters,sizeof(pContext->low_ansi_characters))) { } else { memcpy(pContext->low_ansi_characters,default_low_ansi_characters,DEFAULT_LOW_ANSI_CHARACTERS); } if (retrievefromini(f_inifile,"[DEFAULTGUI]","monochrome_characters",pContext->monochrome_characters,sizeof(pContext->monochrome_characters))) { } else { memcpy(pContext->monochrome_characters,default_monochrome_characters,DEFAULT_MONOCHROME_CHARACTERS); } if (retrievefromini(f_inifile,"[DEFAULTGUI]","sixel_forceresolution",result,sizeof(result))) { if (result[0]=='y' || result[0]=='Y' || result[0]=='t' || result[0]=='T' || result[0]=='1') pContext->forceres=1; // set it to one if the entry reads "yes"/"True"/1 } if (retrievefromini(f_inifile,"[DEFAULTGUI]","sixel_resolution",result,sizeof(result))) { int i; int l; l=strlen(result); pContext->screenwidth=pContext->screenheight=0; for (i=0;iscreenwidth =atoi(&result[0]); pContext->screenheight=atoi(&result[i+1]); } } if (pContext->screenwidth==0 || pContext->screenwidth>MAX_SRESX || pContext->screenheight==0) { printf("illegal parameter for sixelresultion. please use something like 1024x768\n"); return DEFAULT_NOK; } } } if (argc) { char result[64]; if (retrievefromcommandline(argc,argv,"-valign",result,sizeof(result))) { if (strncmp(result,"left",4)==0) pContext->textalign=0; else if (strncmp(result,"block",5)==0) pContext->textalign=1; else if (strncmp(result,"right",5)==0) pContext->textalign=2; else { printf("unknown parameter for -valign. please use one of\n"); printf("left "); printf("block "); printf("right "); printf("\n"); return DEFAULT_NOK; } } if (retrievefromcommandline(argc,argv,"-vmode",result,sizeof(result))) { if (strncmp(result,"none",4)==0) pContext->mode=eMODE_NONE; else if (strncmp(result,"monochrome_inv",14)==0) {pContext->mode=eMODE_MONOCHROME;pContext->monochrome_inverted=1;} else if (strncmp(result,"monochrome",10)==0) pContext->mode=eMODE_MONOCHROME; else if (strncmp(result,"low_ansi2",9)==0) pContext->mode=eMODE_LOW_ANSI2; else if (strncmp(result,"low_ansi",8)==0) pContext->mode=eMODE_LOW_ANSI; else if (strncmp(result,"high_ansi2",10)==0) pContext->mode=eMODE_HIGH_ANSI2; else if (strncmp(result,"high_ansi",9)==0) pContext->mode=eMODE_HIGH_ANSI; else if (strncmp(result,"sixel",5)==0) pContext->mode=eMODE_SIXEL; else if (strncmp(result,"utf",4)==0) pContext->mode=eMODE_UTF; else { printf("unknown parameter for -vmode. please use one of\n"); printf("none "); printf("monochrome "); printf("monochrome_inv "); printf("low_ansi "); printf("low_ansi2 "); printf("high_ansi "); printf("high_ansi2 "); printf("sixel "); printf("utf "); printf("\n"); return DEFAULT_NOK; } } if (retrievefromcommandline(argc,argv,"-vrows",result,sizeof(result))) { int rows; rows=atoi(result); if (rows<1 || rows>500) { printf("illegal parameter for -vrows. please use values between 1 and 500\n"); return DEFAULT_NOK; } pContext->rows=rows; } if (retrievefromcommandline(argc,argv,"-vcols",result,sizeof(result))) { int cols; cols=atoi(result); if (cols<1 || cols>600) { printf("illegal parameter for -vcols. please use values between 1 and 600\n"); return DEFAULT_NOK; } pContext->columns=cols; } if (retrievefromcommandline(argc,argv,"-vecho",NULL,0)) { pContext->echomode=1; } if (retrievefromcommandline(argc,argv,"-vlog",result,sizeof(result))) { fprintf(stderr,"Opening logfile [%s] for writing\n",result); pContext->f_logfile=fopen(result,"wb"); if (pContext->f_logfile==NULL) { fprintf(stderr,"Error opening logfile [%s]\n",result); exit(0); } } if (retrievefromcommandline(argc,argv,"-sres",result,sizeof(result))) { int i; int l; l=strlen(result); pContext->screenwidth=pContext->screenheight=0; for (i=0;iscreenwidth =atoi(&result[0]); pContext->screenheight=atoi(&result[i+1]); } } if (pContext->screenwidth==0 || pContext->screenwidth>MAX_SRESX || pContext->screenheight==0) { printf("illegal parameter for -sres. please use something like 1024x768\n"); return DEFAULT_NOK; } } if (retrievefromcommandline(argc,argv,"-sforce",result,sizeof(result))) { pContext->forceres=1; } } return DEFAULT_OK; } dmagnetic-0.32/src/gui/default_render.h0000644000175000017500000000314614076357617017526 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef DEFAULT_RENDER_H #define DEFAULT_RENDER_H #include "picture.h" int default_render_lowansi(char* allowed,tPicture* picture,int rows,int cols); int default_render_lowansi2(char* allowed,tPicture* picture,int rows,int cols); int default_render_monochrome(char* greyscales,int inverted,tPicture* picture,int rows,int cols); #endif dmagnetic-0.32/src/gui/default_callbacks.h0000644000175000017500000000445314076357617020170 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // the purpose of this file is to provide a callback-fallback until proper // user interfaces have been established. It will do ;) #ifndef LINEA_DEFAULT_CALLBACKS_H #define LINEA_DEFAULT_CALLBACKS_H #include "picture.h" #define DEFAULT_OK 0 #define DEFAULT_NOK -1 // interface to the lineA int default_cbOutputChar(void* context,char c,unsigned char controlD2,unsigned char flag_headline); int default_cbOutputString(void* context,char* string,unsigned char controlD2,unsigned char flag_headline); int default_cbInputString(void* context,int* len,char* string); int default_cbDrawPicture(void* context,tPicture* picture,int mode); int default_cbSaveGame(void* context,char* filename,void* ptr,int len); int default_cbLoadGame(void* context,char* filename,void* ptr,int len); // interface to the main application int default_getsize(int* size); // if there is a .ini-file, extract the section for this user interface. the same goes for the command line parameters int default_open(void* hContext,FILE* f_inifile,int argc,char** argv); #endif dmagnetic-0.32/src/gui/default_render.c0000644000175000017500000003162114076357617017520 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "default_palette.h" #include "default_render.h" #include "picture.h" // the following table is a bitmap, representing all the available ASCII characters. const unsigned long long asciibitmap[95]={ 0x0000000000000000, // 0x00180018183c3c18, // ! 0x0000000000003636, // " 0x0036367f367f3636, // # 0x000c1f301e033e0c, // $ 0x0063660c18336300, // % 0x006e333b6e1c361c, // & 0x0000000000030606, // ' 0x00180c0606060c18, // ( 0x00060c1818180c06, // ) 0x0000663cff3c6600, // * 0x00000c0c3f0c0c00, // + 0x060c0c0000000000, // , 0x000000003f000000, // - 0x000c0c0000000000, // . 0x000103060c183060, // / 0x003e676f7b73633e, // 0 0x003f0c0c0c0c0e0c, // 1 0x003f33061c30331e, // 2 0x001e33301c30331e, // 3 0x0078307f33363c38, // 4 0x001e3330301f033f, // 5 0x001e33331f03061c, // 6 0x000c0c0c1830333f, // 7 0x001e33331e33331e, // 8 0x000e18303e33331e, // 9 0x000c0c00000c0c00, // : 0x060c0c00000c0c00, // ; 0x00180c0603060c18, // < 0x00003f00003f0000, // = 0x00060c1830180c06, // > 0x000c000c1830331e, // ? 0x001e037b7b7b633e, // @ 0x0033333f33331e0c, // A 0x003f66663e66663f, // B 0x003c66030303663c, // C 0x001f36666666361f, // D 0x007f46161e16467f, // E 0x000f06161e16467f, // F 0x007c66730303663c, // G 0x003333333f333333, // H 0x001e0c0c0c0c0c1e, // I 0x001e333330303078, // J 0x006766361e366667, // K 0x007f66460606060f, // L 0x0063636b7f7f7763, // M 0x006363737b6f6763, // N 0x001c36636363361c, // O 0x000f06063e66663f, // P 0x00381e3b3333331e, // Q 0x006766363e66663f, // R 0x001e33380e07331e, // S 0x001e0c0c0c0c2d3f, // T 0x003f333333333333, // U 0x000c1e3333333333, // V 0x0063777f6b636363, // W 0x0063361c1c366363, // X 0x001e0c0c1e333333, // Y 0x007f664c1831637f, // Z 0x001e06060606061e, // [ 0x00406030180c0603, // BACKSLASH 0x001e18181818181e, // ] 0x0000000063361c08, // ^ 0xff00000000000000, // _ 0x0000000000180c0c, // ` 0x006e333e301e0000, // a 0x003b66663e060607, // b 0x001e3303331e0000, // c 0x006e33333e303038, // d 0x001e033f331e0000, // e 0x000f06060f06361c, // f 0x1f303e33336e0000, // g 0x006766666e360607, // h 0x001e0c0c0c0e000c, // i 0x1e33333030300030, // j 0x0067361e36660607, // k 0x001e0c0c0c0c0c0e, // l 0x00636b7f7f330000, // m 0x00333333331f0000, // n 0x001e3333331e0000, // o 0x0f063e66663b0000, // p 0x78303e33336e0000, // q 0x000f06666e3b0000, // r 0x001f301e033e0000, // s 0x00182c0c0c3e0c08, // t 0x006e333333330000, // u 0x000c1e3333330000, // v 0x00367f7f6b630000, // w 0x0063361c36630000, // x 0x1f303e3333330000, // y 0x003f260c193f0000, // z 0x00380c0c070c0c38, // { 0x0018181800181818, // | 0x00070c0c380c0c07, // } 0x0000000000003b6e // ~ }; int default_render_lowansi(char* allowed,tPicture* picture,int rows,int cols) { unsigned char maxplut[16]={0}; int cnt0; int x,y,a,c,i,j,k,p; // counting variables int minccnt,maxc; int maxpcnt,maxp; int accux,accuy; cnt0=0; // step one: // find a good substitute for the palette. if (picture->pictureType==PICTURE_C64) { // the c64 had a fixed palette. maxplut[ 0]=0x0;maxplut[ 1]=0xe;maxplut[ 2]=0x1;maxplut[ 3]=0x6; // black, white, red, cyan maxplut[ 4]=0x5;maxplut[ 5]=0x2;maxplut[ 6]=0x4;maxplut[ 7]=0xb; // purple, green, blue, yellow maxplut[ 8]=0x9;maxplut[ 9]=0x3;maxplut[10]=0xd;maxplut[11]=0x8; // orange, brown, pink, dark grey maxplut[12]=0x7;maxplut[13]=0xa;maxplut[14]=0xc;maxplut[15]=0xf; // medium grey, light green, light blue, light grey } else { default_palette(picture,maxplut); } // step 2: render the picture. use the color and the character that best represents a 8x8 block. y=0; accux=accuy=0; for (i=0;iheight;i++) { accuy+=rows; // since the output is smaller than the actual picture, count a few lines. // at some point, there are enough to print one character. if (accuy>=picture->height) { int lastmaxp; lastmaxp=-1; accux=0; x=0; for (j=0;jwidth;j++) { accux+=cols; if (accux>=picture->width) { unsigned long long pb; int scalex,scaley; scalex=(j-x)/8; scaley=(i-y)/8; if (scalex==0) scalex=1; if (scaley==0) scaley=1; // at this point, a rectangle from X:x..j, Y:y..i is being rendered. // first: find the most common characters and the most common color. // find within the rectangle the largest amount of pixels with the same color maxp=0; maxpcnt=0; for (p=0;p<16;p++) { int x2,y2; cnt0=0; for (x2=x;x2=0 && x2width && y2>=0 && y2height) if (picture->pixels[y2*picture->width+x2]==p) cnt0++; } } if (cnt0>maxpcnt) { maxpcnt=cnt0; maxp=p; } } // create a bitmap of the next 8x8 pixel block pb=0; for (k=0;k<64;k++) { int x2,y2; int line,row; row=k/8; line=k%8; x2=x+scalex*4-scalex*line; y2=y+scaley*4-scaley*row; pb<<=1; if (x2>=0 && x2width && y2>=0 && y2height) if (picture->pixels[y2*picture->width+x2]==maxp) pb|=1; } // compare the bitmap to the bitmap for a character. // find the one with the lowest number of mismatches minccnt=64; maxc=0; for (a=0;a>=1; } if ((cnt0)<=minccnt) { minccnt=cnt0; maxc=c; } } maxp=maxplut[maxp]; if (maxp!=lastmaxp) printf("\x1b[%d;%dm",maxp/8,30+maxp%8); lastmaxp=maxp; if (maxc<32 || maxc>=127) printf(" "); else printf("%c",maxc); accux-=picture->width; x=j; // the next rectangle will begin at the edge of this one. } } accuy-=picture->height; y=i; // the next rectangle will begin in this line printf("\x1b[0m\n"); } } return 0; } int default_render_lowansi2(char* allowed,tPicture* picture,int rows,int cols) { int cnt0; int x,y,a,c,i,j,k,p; // counting variables int minccnt,maxc; int maxpcnt,maxp; int accux,accuy; int redsum,greensum,bluesum,pixcnt; int lastx,lasty; cnt0=0; // render the picture. use the color and the character that best represents a 8x8 block. y=0; accux=accuy=0; lasty=0; for (i=0;iheight;i++) { accuy+=rows; // since the output is smaller than the actual picture, count a few lines. // at some point, there are enough to print one character. if (accuy>=picture->height) { int lastmaxp; lastmaxp=-1; accux=0; x=0; lastx=0; for (j=0;jwidth;j++) { accux+=cols; if (accux>=picture->width) { unsigned long long pb; int scalex,scaley; int l; int maxp2; int mul; redsum=greensum=bluesum=pixcnt=0; for (k=lastx;k<=j;k++) { for (l=lasty;l<=i;l++) { unsigned int p; p=picture->palette[(int)picture->pixels[k+l*picture->width]]; redsum +=(PICTURE_GET_RED(p)); greensum+=(PICTURE_GET_GREEN(p)); bluesum +=(PICTURE_GET_BLUE(p)); pixcnt++; } } lastx=j; maxp2=default_findrgbcluster(redsum/pixcnt,greensum/pixcnt,bluesum/pixcnt); if (picture->pictureType==PICTURE_HALFTONE) mul=2; else mul=1; scalex=(j-x)/(8*mul); scaley=(i-y)/(8*mul); if (scalex==0) scalex=1; if (scaley==0) scaley=1; // at this point, a rectangle from X:x..j, Y:y..i is being rendered. // first: find the most common characters and the most common color. // find within the rectangle the largest amount of pixels with the same color maxp=0; maxpcnt=0; for (p=0;p<16;p++) { int x2,y2; cnt0=0; for (x2=x;x2=0 && x2width && y2>=0 && y2height) if (picture->pixels[y2*picture->width+x2]==p) cnt0++; } } if (cnt0>maxpcnt) { maxpcnt=cnt0; maxp=p; } } // create a bitmap of the next 8x8 pixel block pb=0; for (k=0;k<64;k++) { int x2,y2; int line,row; row=k/(8*mul); line=k%(8*mul); x2=x+scalex*(4*mul)-scalex*line; y2=y+scaley*(1*mul)-scaley*row; pb<<=1; if (x2>=0 && x2width && y2>=0 && y2height) if (picture->pixels[y2*picture->width+x2]==maxp) pb|=1; } // compare the bitmap to the bitmap for a character. // find the one with the lowest number of mismatches minccnt=64; maxc=0; for (a=0;a>=1; } if ((cnt0)<=minccnt) { minccnt=cnt0; maxc=c; } } if (maxp2!=lastmaxp) printf("\x1b[%d;%dm",maxp2/8,30+maxp2%8); lastmaxp=maxp2; if (maxc<32 || maxc>=127) printf(" "); else printf("%c",maxc); accux-=picture->width; x=j; // the next rectangle will begin at the edge of this one. } } lasty=i; accuy-=picture->height; y=i; // the next rectangle will begin in this line printf("\x1b[0m\n"); } } return 0; } int default_render_monochrome(char* greyscales,int inverted,tPicture* picture,int rows,int cols) { int i; int j; int k,l; int y_up,y_down,x_left,x_right; int accux,accuy; int grey; int mingrey,maxgrey; int cnt; int pixcnt; int p; int scalenum=strlen(greyscales); int pass; // first: try to find the brightest/darkest pixels // second pass: the position of the character within the monochrome ramp should be proportional to the brightness of a "pixel". cnt=0; mingrey=maxgrey=0; for (pass=0;pass<2;pass++) { if (pass==1 && mingrey==maxgrey) { maxgrey++; } y_up=0; accux=accuy=0; for (i=0;iheight;i++) { accuy+=rows; if (accuy>=picture->height || i==picture->height-1) { accuy-=picture->height; y_down=i+1; x_left=0; for (j=0;jwidth;j++) { accux+=cols; if (accux>=picture->width || j==picture->width-1) { x_right=j+1; accux-=picture->width; // at this point, a rectangle between y_up,y_down, x_left,x_right contains the pixels that need to be greyscaled. grey=0; pixcnt=0; for (k=y_up;kpixels[k*picture->width+l]; // compensate for luminance (0.2126*R + 0.7152*G + 0.0722*B) grey+=2126*(PICTURE_GET_RED(picture->palette[p])); grey+=7152*(PICTURE_GET_GREEN(picture->palette[p])); grey+= 722*(PICTURE_GET_BLUE(picture->palette[p])); pixcnt++; } } grey/=pixcnt; if (pass==0) { // first pass: find the dimmest and the brightest "pixels" if (cnt==0 || grey>maxgrey) maxgrey=grey; if (cnt==0 || grey>(2*PICTURE_BITS_PER_RGB_CHANNEL))&PICTURE_MAX_RGB_VALUE) #define PICTURE_GET_GREEN(p) (((p)>>(1*PICTURE_BITS_PER_RGB_CHANNEL))&PICTURE_MAX_RGB_VALUE) #define PICTURE_GET_BLUE(p) (((p)>>(0*PICTURE_BITS_PER_RGB_CHANNEL))&PICTURE_MAX_RGB_VALUE) typedef enum _ePictureType { PICTURE_DEFAULT, PICTURE_HALFTONE, PICTURE_C64 } ePictureType; // the purpose of this file is to provide the data structure for the pictures. typedef struct _tPicture { unsigned int palette[16]; int height; int width; char pixels[262144]; ePictureType pictureType; } tPicture; #endif dmagnetic-0.32/src/toplevel/dMagnetic.c0000644000175000017500000002330514076357617017476 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "version.h" #include "configuration.h" #include "vm68k.h" #include "linea.h" #include "default_callbacks.h" #include "maggfxloader.h" #include "dMagnetic_helpscreens.h" #include "dMagnetic_pathnames.h" #define SHAREDMEMSIZE 98304 // 98304 bytes ought to be enough for everybody #define MAXMAGSIZE (1<<20) #define MAXGFXSIZE (1<<22) int dMagnetic_init(void** hVM68k,void** hLineA,void* pSharedMem,int memsize,char* magbuf,int magsize,char* gfxbuf,int gfxsize) { int sizevm68k; int sizelineA; int retval; int version; // step 2: start the engine. retval=vm68k_getsize(&sizevm68k); if (retval) { fprintf(stderr,"ERROR: vm68k_getsize returned %d\n",retval); return retval; } retval=lineA_getsize(&sizelineA); if (retval) { fprintf(stderr,"ERROR: lineA_getsize returned %d\n",retval); return retval; } *hVM68k=malloc(sizevm68k); *hLineA=malloc(sizelineA); if (*hVM68k==NULL || *hLineA==NULL) { fprintf(stderr,"ERROR: unable to allocate memory for the engine\n"); return -1; } retval=lineA_init(*hLineA,pSharedMem,&memsize,magbuf,magsize,gfxbuf,gfxsize); if (retval) { fprintf(stderr,"ERROR: lineA_init returned %d\n",retval); return retval; } retval=lineA_getVersion(*hLineA,&version); if (retval) { fprintf(stderr,"ERROR: lineA_getversion returned %d\n",retval); return retval; } fprintf(stderr,"Initializing the 68K version %d VM\n",version); if (version==4) { fprintf(stderr,"\n"); fprintf(stderr,"---------------------------------------\n"); fprintf(stderr,"- Version 4 of the VM requires you to -\n"); fprintf(stderr,"- activate the graphics manually. Use -\n"); fprintf(stderr,"- the command GRAPHICS now -\n"); fprintf(stderr,"---------------------------------------\n"); fprintf(stderr,"\n"); } retval=vm68k_init(*hVM68k,pSharedMem,memsize,version); if (retval) { fprintf(stderr,"ERROR: vm68k_init returned %d\n",retval); return retval; } return 0; } int main(int argc,char** argv) { int i; char inifilename[1024]; int retval; FILE *f_inifile=NULL; void* hVM68k; void* hLineA; void* hGUI; void* pSharedMem; int sizeGUI; int unknownopcode; char *homedir; char random_mode; unsigned int random_seed; int egamode; char* magbuf; char* gfxbuf; // figure out the location of the inifile. if (!(retrievefromcommandline(argc,argv,"--version",NULL,0))) { dMagnetic_helpscreens_header(); #define LOCNUM 14 const char *locations[LOCNUM]={ PATH_ETC, PATH_USR_LOCAL_SHARE, PATH_USR_LOCAL_SHARE_GAMES, PATH_USR_LOCAL_SHARE"dMagnetic/", PATH_USR_LOCAL_GAMES, PATH_USR_LOCAL_GAMES"dMagnetic/", PATH_USR_SHARE, PATH_USR_SHARE_GAMES, PATH_USR_SHARE"dMagnetic/", PATH_USR_GAMES, PATH_USR_GAMES"dMagnetic/", PATH_USR_SHARE"doc/dmagnetic/", PATH_USR_PKG_SHARE"doc/dMagnetic/", "./"}; // this should always be the last one. f_inifile=NULL; if (f_inifile==NULL) { homedir=getenv("HOME"); snprintf(inifilename,1023,"%s/dMagnetic.ini",homedir); f_inifile=fopen(inifilename,"rb"); } for (i=0;i0x7fffffff) { printf("illegal random seed. please use a value between %d and %d\n",1,0x7fffffff); return 1; } } } if (argc) { char result[64]; if (retrievefromcommandline(argc,argv,"-rmode",result,sizeof(result))) { if (result[0]=='p') random_mode=0; else if (result[0]=='r') random_mode=1; else { printf("illegal parameter for -rmode. please use one of "); printf("pseudo "); printf("real "); printf("\n"); return 1; } } if (retrievefromcommandline(argc,argv,"-rseed",result,sizeof(result))) { random_seed=atoi(result); if (random_seed<1 || random_seed>0x7fffffff) { printf("illegal parameter for -rseed. please use a value between %d and %d\n",1,0x7fffffff); return 1; } } } egamode=0; if (f_inifile) { char result[64]; if (retrievefromcommandline(argc,argv,"-ega",result,sizeof(result))) { egamode=1; } } if (f_inifile) fclose(f_inifile); magbuf=malloc(MAXMAGSIZE); gfxbuf=malloc(MAXGFXSIZE); // this is the main loop. do { int magsize; int gfxsize; magsize=MAXMAGSIZE; gfxsize=MAXGFXSIZE; if (magbuf==NULL || gfxbuf==NULL) { fprintf(stderr,"ERROR: unable to allocate memory for the data files\n"); return -1; } f_inifile=fopen(inifilename,"rb"); if (loader_init(argc,argv,f_inifile, magbuf,&magsize,gfxbuf,&gfxsize)) { dMagnetic_helpscreens_loaderfailed(argv[0]); return 1; } retval=dMagnetic_init(&hVM68k,&hLineA,pSharedMem,SHAREDMEMSIZE,magbuf,magsize,gfxbuf,gfxsize); if (retval) { return 1; } retval|=lineA_configrandom(hLineA,random_mode,random_seed); retval|=lineA_setEGAMode(hLineA,egamode); // set the call back hooks for this GUI retval|=lineA_setCBoutputChar(hLineA,default_cbOutputChar, hGUI); retval|=lineA_setCBoutputString(hLineA,default_cbOutputString, hGUI); retval|=lineA_setCBinputString(hLineA,default_cbInputString, hGUI); retval|=lineA_setCBDrawPicture(hLineA,default_cbDrawPicture, hGUI); retval|=lineA_setCBLoadGame(hLineA,default_cbLoadGame, hGUI); retval|=lineA_setCBSaveGame(hLineA,default_cbSaveGame, hGUI); if (retval) { fprintf(stderr,"ERROR: setting the API hooks failed\n"); return 1; } ///////////////////////////////////////////// lineA_showTitleScreen(hLineA); // some versions of the game have a title screen. // everything is good. have fun playing! do { unsigned short opcode; unknownopcode=0; retval=vm68k_getNextOpcode(hVM68k,&opcode); if (retval==VM68K_OK) retval=lineA_substitute_aliases(hLineA,&opcode); if (retval==LINEA_OK) retval=vm68k_singlestep(hVM68k,opcode); if (retval!=VM68K_OK) retval=lineA_singlestep(hLineA,hVM68k,opcode); if (retval!=LINEA_OK && retval!=LINEA_OK_QUIT && retval!=LINEA_OK_RESTART) { fprintf(stderr,"\x1b[0m\n\x1b[0;37;44m Sorry. Unknown opcode %04X\x1b[0m\n",opcode); unknownopcode=1; } // if (retval==LINEA_OK_QUIT) printf("\x1b[0m\n\x1b[0;37;44mGoodbye!\x1b[0m\n"); } while (!unknownopcode && !retval); } while (retval==LINEA_OK_RESTART); free(gfxbuf); free(magbuf); // this concludes the main loop free(pSharedMem); free(hGUI); free(hLineA); free(hVM68k); return 0; } dmagnetic-0.32/src/toplevel/configuration.c0000644000175000017500000000677514076357617020466 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "configuration.h" int retrievefromini(FILE *f,char* section,char* entry,char* retstring,int retstringspace) { char line[1024]; int i; int j; int l,ls,le; char lc; int state; int found; found=0; state=0; // state 0: search for the [section] // state 1: search for the entry= // state 2: done ls=strlen(section); le=strlen(entry); fseek(f,0,SEEK_SET); state=0; do { if (fgets(line,sizeof(line),f)==NULL) return 0; l=strlen(line); j=0; lc=0; // reduce the line: remove spaces, unless they are escaped. for (i=0;i=32) // escaped { line[j++]=c; // keep this character } else if (c==';' || c<32) { line[j++]=0; // terminate the line here } else if (c>32 && c!='\\') { line[j++]=c; } lc=c; } line[j]=0; l=strlen(line); if (l) { if (line[0]=='[' && state==0) // this is a section, see if it is a match. { int match; match=(ls==l); // cannot be a match for (i=0;ile && line[le]=='=') match=1; for (i=0;i(l-le)) { memcpy(retstring,&line[le+1],l-le); found=1; } } } } while (!feof(f) && state!=2 && !found); return found; } int retrievefromcommandline(int argc,char** argv,char* parameter, char* retstring,int retstringspace) { int i; int found; found=0; for (i=0;istrlen(argv[i+1])) { memcpy(retstring,argv[i+1],strlen(argv[i+1])+1); } else found=-2; } } } } } return found; } dmagnetic-0.32/src/toplevel/configuration.h0000644000175000017500000000301714076357617020455 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CONFIGURATION_H #define CONFIGURATION_H int retrievefromini(FILE *f,char* section,char* entry,char* retstring,int retstringsize); int retrievefromcommandline(int argc,char** argv,char* parameter, char* retstring,int retstringspace); #endif dmagnetic-0.32/src/toplevel/dMagnetic_helpscreens.c0000644000175000017500000003140714076357617022073 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "dMagnetic_helpscreens.h" #include "dMagnetic_pathnames.h" #include "version.h" #define NUMGAMES 7 #define NUMPLATFORMS 10 #define PLATFORM_MAG (1<<0) #define PLATFORM_GFX (1<<1) #define PLATFORM_MSDOS (1<<2) #define PLATFORM_TWORSC (1<<3) #define PLATFORM_D64 (1<<4) #define PLATFORM_AMSTRADCPC (1<<5) #define PLATFORM_SPECTRUM (1<<6) #define PLATFORM_ARCHIMEDES (1<<7) #define PLATFORM_ATARIXL (1<<8) #define PLATFORM_APPLEII (1<<9) typedef struct _tPlatformInfo { char* name; char* dir; char* suffix; char uppercase; char special; int disksexpected; // some games require komma seperated names for the images. PAWN1.D64,PAWN2.D64, for example int active; unsigned short mask; char *cmdline; } tPlatformInfo; typedef struct _tGameInfo { char *name; char *description; char *maggfxname; int disknum; int special; char *specialPrefix; unsigned short ported; } tGameInfo; const tPlatformInfo cdMagnetic_platformInfo[NUMPLATFORMS]={ {"mag", PATH_USR_LOCAL_SHARE_GAMES"magneticscrolls/", ".mag" ,0,0,1,1, PLATFORM_MAG, "-mag MAGFILE.mag"}, {"gfx", PATH_USR_LOCAL_SHARE_GAMES"magneticscrolls/", ".gfx" ,0,0,1,1, PLATFORM_GFX, "-gfx GFXFILE.gfx"}, {"msdos", "/MSDOS/C/", "" ,1,0,1,0, PLATFORM_MSDOS, "-msdosdir DIRECTORY/"}, {"tworsc", PATH_USR_LOCAL_SHARE"games/", "TWO.RSC",0,1,1,0, PLATFORM_TWORSC, "-tworsc DIRECTORY/TWO.RSC"}, {"d64", "/8/", ".D64" ,1,0,2,0, PLATFORM_D64, "-d64 IMAGE1.d64,IMAGE2.d64"}, {"amstradcpc", "/dsk/amstradcpc/", ".DSK" ,1,0,2,0, PLATFORM_AMSTRADCPC, "-amstradcpc IMAGE1.DSK,IMAGE2.DSK"}, {"spectrum", "/dsk/spectrum/", ".DSK" ,0,0,1,0, PLATFORM_SPECTRUM, "-spectrum IMAGE.DSK"}, {"archimedes", "/adf/", ".adf" ,0,0,1,0, PLATFORM_ARCHIMEDES, "-archimedes IMAGE.adf"}, {"atarixl", "/atr/", ".ATR" ,1,0,2,0, PLATFORM_ATARIXL, "-atarixl IMAGE1.atr,IMAGE2.atr"}, {"appleii", "/appleii/", ".NIB" ,1,0,3,0, PLATFORM_APPLEII, "-appleii IMAGE1.NIB,IMAGE2.NIB,IMAGE3.NIB"} }; const tGameInfo cdMagnetic_gameInfo[NUMGAMES]={ {"pawn", "The Pawn", "pawn", 2,0,"" ,PLATFORM_MAG|PLATFORM_GFX|PLATFORM_MSDOS|PLATFORM_D64|PLATFORM_AMSTRADCPC|PLATFORM_SPECTRUM|PLATFORM_ARCHIMEDES|PLATFORM_ATARIXL |PLATFORM_APPLEII}, {"guild", "The Guild of Thieves", "guild", 2,1,"MSC/G" ,PLATFORM_MAG|PLATFORM_GFX|PLATFORM_MSDOS|PLATFORM_D64|PLATFORM_AMSTRADCPC|PLATFORM_SPECTRUM|PLATFORM_ARCHIMEDES|PLATFORM_ATARIXL|PLATFORM_TWORSC|PLATFORM_APPLEII}, {"jinxter", "Jinxter", "jinxter", 2,0,"" ,PLATFORM_MAG|PLATFORM_GFX|PLATFORM_MSDOS|PLATFORM_D64|PLATFORM_AMSTRADCPC|PLATFORM_SPECTRUM|PLATFORM_ARCHIMEDES|PLATFORM_ATARIXL |PLATFORM_APPLEII}, {"corruption", "Corruption", "corrupt", 3,1,"MSC/C" ,PLATFORM_MAG|PLATFORM_GFX|PLATFORM_MSDOS|PLATFORM_D64|PLATFORM_AMSTRADCPC|PLATFORM_SPECTRUM|PLATFORM_ARCHIMEDES |PLATFORM_TWORSC|PLATFORM_APPLEII}, {"fish", "Fish!", "fish", 2,1,"MSC/F" ,PLATFORM_MAG|PLATFORM_GFX|PLATFORM_MSDOS|PLATFORM_D64|PLATFORM_AMSTRADCPC|PLATFORM_SPECTRUM|PLATFORM_ARCHIMEDES |PLATFORM_TWORSC}, {"myth", "Myth", "myth", 1,0,"" ,PLATFORM_MAG|PLATFORM_GFX|PLATFORM_MSDOS|PLATFORM_D64|PLATFORM_AMSTRADCPC|PLATFORM_SPECTRUM }, {"wonderland", "Wonderland", "wonder", 1,1,"wonderland/" ,PLATFORM_MAG|PLATFORM_GFX |PLATFORM_TWORSC}, }; void dMagnetic_helpscreens_header() { fprintf(stderr,"*** dMagnetic %d.%d%d\n",VERSION_MAJOR,VERSION_MINOR,VERSION_REVISION); fprintf(stderr,"*** Use at your own risk\n"); fprintf(stderr,"*** (C)opyright 2021 by dettus@dettus.net\n"); fprintf(stderr,"*****************************************\n"); fprintf(stderr,"\n"); } void dMagnetic_helpscreens_license() { printf("Copyright 2021, dettus@dettus.net\n"); printf("\n"); printf("Redistribution and use in source and binary forms, with or without modification,\n"); printf("are permitted provided that the following conditions are met:\n"); printf("\n"); printf("1. Redistributions of source code must retain the above copyright notice, this \n"); printf(" list of conditions and the following disclaimer.\n"); printf("\n"); printf("2. Redistributions in binary form must reproduce the above copyright notice, \n"); printf(" this list of conditions and the following disclaimer in the documentation \n"); printf(" and/or other materials provided with the distribution.\n"); printf("\n"); printf("THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n"); printf("ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED \n"); printf("WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n"); printf("DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE \n"); printf("FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL \n"); printf("DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR \n"); printf("SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n"); printf("CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, \n"); printf("OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE \n"); printf("OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"); printf("\n"); } void dMagnetic_helpscreens_help(char* argv0) { int i; printf("To start a game, run\n"); printf("%s [-ini dMagnetic.ini] GAME\n",argv0); printf("where GAME is one of \n ["); for (i=0;i #include #include "vm68k_datatypes.h" #include "vm68k_decode.h" // the purpose of this function is to perform a pattern matching to the instruction, and return the enumeration value. // the more bits are constant, the higher should be the matche's priority. tVM68k_instruction vm68k_decode(tVM68k_uword opcode) { // instructions with 16 constant bits if ((opcode&0xffff)==0x023c) return VM68K_INST_ANDItoCCR; //ANDItoCCR: 0000 0010 0011 1100 00000000dddddddd if ((opcode&0xffff)==0x027c) return VM68K_INST_ANDItoSR; //ANDItoSR: 0000 0010 0111 1100 dddddddddddddddd if ((opcode&0xffff)==0x0A3C) return VM68K_INST_EORItoCCR; //EORItoCCR: 0000 1010 0011 1100 00000000dddddddd if ((opcode&0xffff)==0x0A7C) return VM68K_INST_EORItoSR; //EORItoSR: 0000 1010 0111 1100 dddddddddddddddd if ((opcode&0xffff)==0x4AFC) return VM68K_INST_ILLEGAL; //ILLEGAL: 0100 1010 1111 1100 if ((opcode&0xffff)==0x4E71) return VM68K_INST_NOP; if ((opcode&0xffff)==0x003C) return VM68K_INST_ORItoCCR; //ORItoCCR: 0000 0000 0011 1100 00000000dddddddd if ((opcode&0xffff)==0x007C) return VM68K_INST_ORItoSR; //ORItoSR: 0000 0000 0111 1100 dddddddddddddddd if ((opcode&0xffff)==0x4E70) return VM68K_INST_RESET; //RESET: 0100 1110 0111 0000 if ((opcode&0xffff)==0x4E73) return VM68K_INST_RTE; //RTE: 0100 1110 0111 0011 if ((opcode&0xffff)==0x4E77) return VM68K_INST_RTR; //RTR: 0100 1110 0111 0111 if ((opcode&0xffff)==0x4E75) return VM68K_INST_RTS; //RTS: 0100 1110 0111 0101 if ((opcode&0xffff)==0x4E72) return VM68K_INST_STOP; //STOP: 0100 1110 0111 0010 iiiiiiiiiiiiiiii if ((opcode&0xffff)==0x4E76) return VM68K_INST_TRAPV; //TRAPV: 0100 1110 0111 0110 // instructions with 13 constant bits if ((opcode&0xfff8)==0x4E50) return VM68K_INST_LINK; //LINK: 0100 1110 0101 0yyy dddddddddddddddd if ((opcode&0xfff8)==0x4840) return VM68K_INST_SWAP; //SWAP: 0100 1000 0100 0yyy if ((opcode&0xfff8)==0x4E58) return VM68K_INST_UNLK; //UNLK: 0100 1110 0101 1yyy // instructions with 12 constant bits if ((opcode&0xfff0)==0x4E40) return VM68K_INST_TRAP; //TRAP: 0100 1110 0100 vvvv if ((opcode&0xfff0)==0x4E60) return VM68K_INST_MOVEUSP; //MOVE USP: 0100 1110 0110 dyyy // instructions with 10 constant bits if ((opcode&0xffc0)==0x0800) return VM68K_INST_BTSTB; //BTST.B: 0000 1000 00mm myyy 0000 0000 bbbb bbbb if ((opcode&0xffc0)==0x0840) return VM68K_INST_BCHGB; //BCHG.B: 0000 1000 01mm myyy if ((opcode&0xffc0)==0x0880) return VM68K_INST_BCLRI; //BCLRI: 0000 1000 10mm myyy if ((opcode&0xffc0)==0x08C0) return VM68K_INST_BSETB; //BSET.B: 0000 1000 11mm myyy if ((opcode&0xffc0)==0x44C0) return VM68K_INST_MOVEtoCCR; //MOVEtoCCR: 0100 0100 11mm myyy if ((opcode&0xffc0)==0x40C0) return VM68K_INST_MOVEfromSR; //MOVEfromSR:0100 0000 11mm myyy if ((opcode&0xffc0)==0x46C0) return VM68K_INST_MOVEtoSR; //MOVEtoSR: 0100 0110 11mm myyy if ((opcode&0xffc0)==0x4840) return VM68K_INST_PEA; //PEA: 0100 1000 01mm myyy if ((opcode&0xffc0)==0x4AC0) return VM68K_INST_TAS; //TAS: 0100 1010 11mm myyy if ((opcode&0xffC0)==0x4EC0) return VM68K_INST_JMP; //JMP: 0100 1110 11mm myyy if ((opcode&0xffC0)==0x4E80) return VM68K_INST_JSR; //JSR: 0100 1110 10mm myyy if ((opcode&0xfe38)==0x4800) return VM68K_INST_EXT; //EXT: 0100 100o oo00 0yyy // instructions with 9 constant bits if ((opcode&0xf0f8)==0x50C8) return VM68K_INST_DBcc; //DBcc: 0101 CCCC 1100 1yyy if ((opcode&0xf1f0)==0xC100) return VM68K_INST_ABCD; //ABCD: 1100 xxx1 0000 myyy if ((opcode&0xf1f0)==0x8100) return VM68K_INST_SBCD; //SBCD: 1000 xxx1 0000 ryyy if ((opcode&0xff80)==0x4880) return VM68K_INST_MOVEMregtomem; //MOVEM: 0100 1000 1smm myyy // reg to mem if ((opcode&0xff80)==0x4C80) return VM68K_INST_MOVEMmemtoreg; //MOVEM: 0100 1100 1smm myyy // mem to reg if ((opcode&0xf1C0)==0x01C0) return VM68K_INST_BSET; //BSET: 0000 xxx1 11mm myyy // instructions with 8 constant bits if ((opcode&0xff00)==0x4200) return VM68K_INST_CLR; //CLR: 0100 0010 ssmm myyy if ((opcode&0xff00)==0x0C00) return VM68K_INST_CMPI; //CMPI: 0000 1100 ssmm myyy if ((opcode&0xff00)==0x0A00) return VM68K_INST_EORI; //EORI: 0000 1010 ssmm myyy if ((opcode&0xff00)==0x0600) return VM68K_INST_ADDI; //ADDI: 0000 0110 ssmm myyy if ((opcode&0xff00)==0x0200) return VM68K_INST_ANDI; //ANDI: 0000 0010 ssmm myyy if ((opcode&0xff00)==0x0000) return VM68K_INST_ORI; //ORI: 0000 0000 ssmm myyy if ((opcode&0xff00)==0x4400) return VM68K_INST_NEG; //NEG: 0100 0100 ssmm myyy if ((opcode&0xff00)==0x4000) return VM68K_INST_NEGX; //NEGX: 0100 0000 ssmm myyy if ((opcode&0xff00)==0x4600) return VM68K_INST_NOT; //NOT: 0100 0110 ssmm myyy if ((opcode&0xff00)==0x0400) return VM68K_INST_SUBI; //SUBI: 0000 0100 ssmm myyy if ((opcode&0xff00)==0x4A00) return VM68K_INST_TST; //TST: 0100 1010 ssmm myyy if ((opcode&0xf0C0)==0xD0C0) return VM68K_INST_ADDA; //ADDA: 1101 rrrs 11mm myyy // IMPORTANT! THIS HAS TO COME BEFORE ADDX! if ((opcode&0xf130)==0xD100) return VM68K_INST_ADDX; //ADDX: 1101 xxx1 ss00 myyy // s=00,01,10=ADDX. 11=ADDA!! if ((opcode&0xf130)==0xC100) return VM68K_INST_EXG; //EXG: 1100 xxx1 oo00 oyyy if ((opcode&0xf0C0)==0x90C0) return VM68K_INST_SUBA; //SUBA: 1001 xxxo 11mm myyy // probably the same problem as ADDA/ADDX. if ((opcode&0xf130)==0x9100) return VM68K_INST_SUBX; //SUBX: 1001 yyy1 ss00 ryyy if ((opcode&0xf0C0)==0xB0C0) return VM68K_INST_CMPA; //CMPA: 1011 xxxo 11mm myyy /// IMPORANT! THIS HAS TO COME BEFORE CMPM! if ((opcode&0xf138)==0xb108) return VM68K_INST_CMPM; //CMPM: 1011 xxx1 ss00 1yyy // instructions with 7 constant bits if ((opcode&0xf1c0)==0x0140) return VM68K_INST_BCHG; //BCHG: 0000 rrr1 01mm myyy if ((opcode&0xf1c0)==0x0180) return VM68K_INST_BCLR; //BCLR: 0000 xxx1 10mm myyy if ((opcode&0xf1C0)==0x0100) return VM68K_INST_BTST; //BTST: 0000 xxx1 00mm myyy if ((opcode&0xf1C0)==0x81C0) return VM68K_INST_DIVS; //DIVS: 1000 xxx1 11mm myyy if ((opcode&0xf1C0)==0x80C0) return VM68K_INST_DIVU; //DIVU: 1000 xxx0 11mm myyy if ((opcode&0xf1C0)==0xC1C0) return VM68K_INST_MULS; //MULS: 1100 xxx1 11mm myyy if ((opcode&0xf1C0)==0xC0C0) return VM68K_INST_MULU; //MULU: 1100 xxx0 11mm myyy if ((opcode&0xf1C0)==0x41C0) return VM68K_INST_LEA; //LEA: 0100 xxx1 11mm myyy if ((opcode&0xf038)==0x0008) return VM68K_INST_MOVEP; //MOVEP: 0000 xxxo oo00 1yyy dddddddddddddddd // instructions with 6 constant bits if ((opcode&0xf018)==0xe018) return VM68K_INST_ROL_ROR; //ROL/ROR: 1110 cccd ssl1 1yyy if ((opcode&0xf018)==0xe010) return VM68K_INST_ROXL_ROXR; //ROXL/ROXR: 1110 cccd ssl1 0yyy if ((opcode&0xf0c0)==0x50C0) return VM68K_INST_SCC; //SCC: 0101 CCCC 11mm myyy if ((opcode&0xf140)==0x4100) return VM68K_INST_CHK; //CHK: 0100 xxx1 s0mm myyy if ((opcode&0xf018)==0xE008) return VM68K_INST_LSL_LSR; //LSL/LSR: 1110 cccd ssl0 1yyy if ((opcode&0xf018)==0xE000) return VM68K_INST_ASL_ASR; //ASL/ASR: 1110 cccd ssl0 0yyy // instructions with 5 constant bits if ((opcode&0xf100)==0x5000) return VM68K_INST_ADDQ; //ADDQ: 0101 ddd0 ssmm myyy if ((opcode&0xf100)==0xb100) return VM68K_INST_EOR; //EOR: 1011 xxx1 oomm myyy if ((opcode&0xf100)==0x7000) return VM68K_INST_MOVEQ; //MOVEQ: 0111 xxx0 dddd dddd if ((opcode&0xf000)==0x9000) return VM68K_INST_SUB; //SUB: 1001 xxx0 oomm myyy if ((opcode&0xf100)==0x5100) return VM68K_INST_SUBQ; //SUBQ: 0101 ddd1 ssmm myyy if ((opcode&0xC1C0)==0x0040) return VM68K_INST_MOVEA; //MOVEA: 00ss xxx0 01mm myyy // instructions with 4 constant bits // if ((opcode&0xf000)==0xD000) return VM68K_INST_ADD; //ADD: 1101 rrro oomm myyy if ((opcode&0xf000)==0xC000) return VM68K_INST_AND; //AND: 1100 xxxo oomm myyy if ((opcode&0xf000)==0x6000) return VM68K_INST_BCC; //BCC: 0110 CCCC dddd dddd if ((opcode&0xf000)==0xB000) return VM68K_INST_CMP; //CMP: 1011 xxx0 oomm myyy if ((opcode&0xf000)==0x8000) return VM68K_INST_OR; //OR: 1000 xxxo oomm myyy // instructions with 2 constant bits if ((opcode&0xc000)==0x0000) return VM68K_INST_MOVE; //MOVE: 00ss xxxm mmMM Myyy return VM68K_INST_UNKNOWN; } #ifdef DEBUG_PRINT void vm68k_get_instructionname(tVM68k_instruction instruction,char* name) { #define INSTFOUND(x) case x: snprintf(name,64,#x); break; switch(instruction) { default: snprintf(name,64,"???"); break; INSTFOUND(VM68K_INST_UNKNOWN) INSTFOUND(VM68K_INST_ABCD) INSTFOUND(VM68K_INST_ADD) INSTFOUND(VM68K_INST_ADDA) INSTFOUND(VM68K_INST_ADDI) INSTFOUND(VM68K_INST_ADDQ) INSTFOUND(VM68K_INST_ADDX) INSTFOUND(VM68K_INST_AND) INSTFOUND(VM68K_INST_ANDI) INSTFOUND(VM68K_INST_ANDItoCCR) INSTFOUND(VM68K_INST_ANDItoSR) INSTFOUND(VM68K_INST_ASL_ASR) INSTFOUND(VM68K_INST_BCC) INSTFOUND(VM68K_INST_BCHG) INSTFOUND(VM68K_INST_BCHGB) INSTFOUND(VM68K_INST_BCLR) INSTFOUND(VM68K_INST_BCLRI) INSTFOUND(VM68K_INST_BRA) INSTFOUND(VM68K_INST_BSET) INSTFOUND(VM68K_INST_BSETB) //INSTFOUND(VM68K_INST_BSR) INSTFOUND(VM68K_INST_BTST) INSTFOUND(VM68K_INST_BTSTB) INSTFOUND(VM68K_INST_CHK) INSTFOUND(VM68K_INST_CLR) INSTFOUND(VM68K_INST_CMP) INSTFOUND(VM68K_INST_CMPA) INSTFOUND(VM68K_INST_CMPI) INSTFOUND(VM68K_INST_CMPM) INSTFOUND(VM68K_INST_DBcc) INSTFOUND(VM68K_INST_DIVS) INSTFOUND(VM68K_INST_DIVU) INSTFOUND(VM68K_INST_EOR) INSTFOUND(VM68K_INST_EORI) INSTFOUND(VM68K_INST_EORItoCCR) INSTFOUND(VM68K_INST_EORItoSR) INSTFOUND(VM68K_INST_EXG) INSTFOUND(VM68K_INST_EXT) INSTFOUND(VM68K_INST_ILLEGAL) INSTFOUND(VM68K_INST_JMP) INSTFOUND(VM68K_INST_JSR) INSTFOUND(VM68K_INST_LEA) INSTFOUND(VM68K_INST_LINK) INSTFOUND(VM68K_INST_LSL_LSR) INSTFOUND(VM68K_INST_MOVE) INSTFOUND(VM68K_INST_MOVEA) INSTFOUND(VM68K_INST_MOVEtoCCR) INSTFOUND(VM68K_INST_MOVEfromSR) INSTFOUND(VM68K_INST_MOVEtoSR) INSTFOUND(VM68K_INST_MOVEUSP) INSTFOUND(VM68K_INST_MOVEMregtomem) INSTFOUND(VM68K_INST_MOVEMmemtoreg) INSTFOUND(VM68K_INST_MOVEP) INSTFOUND(VM68K_INST_MOVEQ) INSTFOUND(VM68K_INST_MULS) INSTFOUND(VM68K_INST_MULU) INSTFOUND(VM68K_INST_NBCD) INSTFOUND(VM68K_INST_NEG) INSTFOUND(VM68K_INST_NEGX) INSTFOUND(VM68K_INST_NOP) INSTFOUND(VM68K_INST_NOT) INSTFOUND(VM68K_INST_OR) INSTFOUND(VM68K_INST_ORI) INSTFOUND(VM68K_INST_ORItoCCR) INSTFOUND(VM68K_INST_ORItoSR) INSTFOUND(VM68K_INST_PEA) INSTFOUND(VM68K_INST_RESET) INSTFOUND(VM68K_INST_ROL_ROR) INSTFOUND(VM68K_INST_ROXL_ROXR) INSTFOUND(VM68K_INST_RTE) INSTFOUND(VM68K_INST_RTR) INSTFOUND(VM68K_INST_RTS) INSTFOUND(VM68K_INST_SBCD) INSTFOUND(VM68K_INST_SCC) INSTFOUND(VM68K_INST_STOP) INSTFOUND(VM68K_INST_SUB) INSTFOUND(VM68K_INST_SUBA) INSTFOUND(VM68K_INST_SUBI) INSTFOUND(VM68K_INST_SUBQ) INSTFOUND(VM68K_INST_SUBX) INSTFOUND(VM68K_INST_SWAP) INSTFOUND(VM68K_INST_TAS) INSTFOUND(VM68K_INST_TRAP) INSTFOUND(VM68K_INST_TRAPV) INSTFOUND(VM68K_INST_TST) INSTFOUND(VM68K_INST_UNLK) } } #endif dmagnetic-0.32/src/engine/vm68k/vm68k_macros.h0000644000175000017500000001455714076357617020526 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef VM68K_MACROS_H #define VM68K_MACROS_H #include "vm68k_datatypes.h" // the gfx2 format introduced MIXED ENDIAN. #define READ_INT32ME(ptr,idx) (\ (((tVM68k_ulong)((ptr)[((idx)+1)])&0xff)<<24) |\ (((tVM68k_ulong)((ptr)[((idx)+0)])&0xff)<<16) |\ (((tVM68k_ulong)((ptr)[((idx)+3)])&0xff)<< 8) |\ (((tVM68k_ulong)((ptr)[((idx)+2)])&0xff)<< 0) |\ 0) #define READ_INT32BE(ptr,idx) (\ (((tVM68k_ulong)((ptr)[((idx)+0)])&0xff)<<24) |\ (((tVM68k_ulong)((ptr)[((idx)+1)])&0xff)<<16) |\ (((tVM68k_ulong)((ptr)[((idx)+2)])&0xff)<< 8) |\ (((tVM68k_ulong)((ptr)[((idx)+3)])&0xff)<< 0) |\ 0) #define READ_INT16BE(ptr,idx) (\ (((tVM68k_ulong)((ptr)[((idx)+0)])&0xff)<< 8) |\ (((tVM68k_ulong)((ptr)[((idx)+1)])&0xff)<< 0) |\ 0) #define READ_INT8BE(ptr,idx) (\ (((tVM68k_ulong)((ptr)[((idx)+0)])&0xff)<< 0) |\ 0) #define READ_INT32LE(ptr,idx) (\ (((unsigned int)((ptr)[((idx)+3)])&0xff)<<24) |\ (((unsigned int)((ptr)[((idx)+2)])&0xff)<<16) |\ (((unsigned int)((ptr)[((idx)+1)])&0xff)<< 8) |\ (((unsigned int)((ptr)[((idx)+0)])&0xff)<< 0) |\ 0) #define READ_INT16LE(ptr,idx) (\ (((unsigned int)((ptr)[((idx)+1)])&0xff)<< 8) |\ (((unsigned int)((ptr)[((idx)+0)])&0xff)<< 0) |\ 0) #define READ_INT8LE(ptr,idx) (\ (((unsigned int)((ptr)[((idx)+0)])&0xff)<< 0) |\ 0) #define WRITE_INT32BE(ptr,idx,val) {\ (ptr)[(idx)+3]=((tVM68k_ubyte)((val)>> 0)&0xff); \ (ptr)[(idx)+2]=((tVM68k_ubyte)((val)>> 8)&0xff); \ (ptr)[(idx)+1]=((tVM68k_ubyte)((val)>>16)&0xff); \ (ptr)[(idx)+0]=((tVM68k_ubyte)((val)>>24)&0xff); \ } #define WRITE_INT16BE(ptr,idx,val) {\ (ptr)[(idx)+1]=((tVM68k_ubyte)((val)>> 0)&0xff); \ (ptr)[(idx)+0]=((tVM68k_ubyte)((val)>> 8)&0xff); \ } #define WRITE_INT8BE(ptr,idx,val) {\ (ptr)[(idx)+0]=((tVM68k_ubyte)((val)>> 0)&0xff); \ } #define WRITE_INT32LE(ptr,idx,val) {\ (ptr)[(idx)+0]=((unsigned char)((val)>> 0)&0xff); \ (ptr)[(idx)+1]=((unsigned char)((val)>> 8)&0xff); \ (ptr)[(idx)+2]=((unsigned char)((val)>>16)&0xff); \ (ptr)[(idx)+3]=((unsigned char)((val)>>24)&0xff); \ } #define WRITE_INT16LE(ptr,idx,val) {\ (ptr)[(idx)+0]=((unsigned char)((val)>> 0)&0xff); \ (ptr)[(idx)+1]=((unsigned char)((val)>> 8)&0xff); \ } #define WRITE_INT8LE(ptr,idx,val) {\ (ptr)[(idx)+0]=((unsigned char)((val)>> 0)&0xff); \ } #define INITNEXT(pVM68k,next) \ (next).pcr=(pVM68k)->pcr; \ (next).a[0]=(pVM68k)->a[0]; \ (next).a[1]=(pVM68k)->a[1]; \ (next).a[2]=(pVM68k)->a[2]; \ (next).a[3]=(pVM68k)->a[3]; \ (next).a[4]=(pVM68k)->a[4]; \ (next).a[5]=(pVM68k)->a[5]; \ (next).a[6]=(pVM68k)->a[6]; \ (next).a[7]=(pVM68k)->a[7]; \ (next).d[0]=(pVM68k)->d[0]; \ (next).d[1]=(pVM68k)->d[1]; \ (next).d[2]=(pVM68k)->d[2]; \ (next).d[3]=(pVM68k)->d[3]; \ (next).d[4]=(pVM68k)->d[4]; \ (next).d[5]=(pVM68k)->d[5]; \ (next).d[6]=(pVM68k)->d[6]; \ (next).d[7]=(pVM68k)->d[7]; \ (next).override_sr=0; \ (next).sr=((pVM68k)->sr); \ (next).cflag=((pVM68k)->sr>>0)&1; \ (next).vflag=((pVM68k)->sr>>1)&1; \ (next).zflag=((pVM68k)->sr>>2)&1; \ (next).nflag=((pVM68k)->sr>>3)&1; \ (next).xflag=((pVM68k)->sr>>4)&1; \ (next).mem_we=0; \ (next).mem_addr[0]=0; \ (next).mem_size=0; \ (next).mem_value[0]=0; #define WRITEFLAGS(pVM68k,transaction) \ (pVM68k)->sr|=(tVM68k_uword)((transaction).cflag)<<0; \ (pVM68k)->sr|=(tVM68k_uword)((transaction).vflag)<<1; \ (pVM68k)->sr|=(tVM68k_uword)((transaction).zflag)<<2; \ (pVM68k)->sr|=(tVM68k_uword)((transaction).nflag)<<3; \ (pVM68k)->sr|=(tVM68k_uword)((transaction).xflag)<<4; #define READEXTENSIONBYTE(pVM68k,pNext) READ_INT8BE((pVM68k)->pMem,(pNext)->pcr+1);(pNext)->pcr+=2; #define READEXTENSIONWORD(pVM68k,pNext) READ_INT16BE((pVM68k)->pMem,(pNext)->pcr);(pNext)->pcr+=2; #define READEXTENSIONLONG(pVM68k,pNext) READ_INT32BE((pVM68k)->pMem,(pNext)->pcr);(pNext)->pcr+=4; #define READEXTENSION(pVM68k,pNext,datatype,operand) \ switch (datatype) \ { \ case VM68K_BYTE: operand=READEXTENSIONBYTE(pVM68k,pNext);break; \ case VM68K_WORD: operand=READEXTENSIONWORD(pVM68k,pNext);break; \ case VM68K_LONG: operand=READEXTENSIONLONG(pVM68k,pNext);break; \ default: operand=0;break; \ } #define READSIGNEDEXTENSION(pVM68k,pNext,datatype,operand) \ switch (datatype) \ { \ case VM68K_BYTE: operand=READEXTENSIONBYTE(pVM68k,pNext);operand=(tVM68k_slong)((tVM68k_sbyte)((operand)& 0xff));break; \ case VM68K_WORD: operand=READEXTENSIONBYTE(pVM68k,pNext);operand=(tVM68k_slong)((tVM68k_sword)((operand)&0xffff));break; \ case VM68K_LONG: operand=READEXTENSIONLONG(pVM68k,pNext);break; \ } #define PUSHWORDTOSTACK(pVM68k,pNext,x) {(pNext)->a[7]-=2;(pNext)->mem_addr[(pNext)->mem_we]=(pNext)->a[7];(pNext)->mem_size=VM68K_WORD;(pNext)->mem_value[(pNext)->mem_we]=x;(pNext)->mem_we++;} #define PUSHLONGTOSTACK(pVM68k,pNext,x) {(pNext)->a[7]-=4;(pNext)->mem_addr[(pNext)->mem_we]=(pNext)->a[7];(pNext)->mem_size=VM68K_LONG;(pNext)->mem_value[(pNext)->mem_we]=x;(pNext)->mem_we++;} #define POPWORDFROMSTACK(pVM68k,pNext,x) {tVM68k_uword y;y=READ_INT16BE((pVM68k)->pMem,(pNext)->a[7]);(pNext)->a[7]+=2;x=((x)&0xffff0000)|(y&0xffff);} #define POPLONGFROMSTACK(pVM68k,pNext,x) {x=READ_INT32BE((pVM68k)->pMem,(pNext)->a[7]);(pNext)->a[7]+=4;} #endif dmagnetic-0.32/src/engine/vm68k/vm68k_loadstore.c0000644000175000017500000002602014076357617021215 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "vm68k.h" #include "vm68k_decode.h" #include "vm68k_datatypes.h" #include "vm68k_macros.h" #include "vm68k_loadstore.h" int vm68k_getbytesize(tVM68k_types size) { switch(size) { case VM68K_BYTE: return 1;break; case VM68K_WORD: return 2;break; case VM68K_LONG: return 4;break; default: return 0; } return 0; } // the way addresses are stored here is that memory addresses are >=0. <=0 addresses the registers. int vm68k_resolve_ea(tVM68k* pVM68k,tVM68k_next *pNext,tVM68k_types size, tVM68k_addrmodes addrmode,tVM68k_ubyte reg, tVM68k_uword legal,tVM68k_slong* ea) { tVM68k_sbyte bytesize; int retval; retval=VM68K_NOK_UNKNOWN_INSTRUCTION; bytesize=vm68k_getbytesize(size); if (addrmode==VM68K_AM_EXT) { switch ((tVM68k_addrmode_ext)reg) { case VM68K_AMX_W: if (legal&VM68K_LEGAL_AMX_W) { *ea=(tVM68k_sword)READEXTENSIONWORD(pVM68k,pNext); retval=VM68K_OK; } break; case VM68K_AMX_L: if (legal&VM68K_LEGAL_AMX_L) { *ea=(tVM68k_slong)READEXTENSIONLONG(pVM68k,pNext); retval=VM68K_OK; } break; case VM68K_AMX_data: if (legal&VM68K_LEGAL_AMX_DATA) { *ea=pNext->pcr; retval=VM68K_OK; switch (size) { case VM68K_BYTE: *ea+=1;pNext->pcr+=2;break; case VM68K_WORD: pNext->pcr+=2;break; case VM68K_LONG: pNext->pcr+=4;break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION; } } break; case VM68K_AMX_PC: if (legal&VM68K_LEGAL_AMX_PC) { *ea=READEXTENSIONWORD(pVM68k,pNext); *ea=(tVM68k_sword)(*ea)+pVM68k->pcr; retval=VM68K_OK; } break; case VM68K_AMX_INDEX_PC: if (legal&VM68K_LEGAL_AMX_INDEX_PC) { *ea=READEXTENSIONWORD(pVM68k,pNext); *ea=*ea+pVM68k->pcr; *ea=*ea+pVM68k->a[reg]*bytesize; // TODO: data or addrreg? retval=VM68K_NOK_UNKNOWN_INSTRUCTION; // TODO: lets decide when we stumble upon this mode } break; } } else { switch (addrmode) { case VM68K_AM_DATAREG: if (legal&VM68K_LEGAL_AM_DATAREG) { *ea=DATAREGADDR(reg); retval=VM68K_OK; } break; case VM68K_AM_ADDRREG: if (legal&VM68K_LEGAL_AM_ADDRREG) { *ea=ADDRREGADDR(reg); retval=VM68K_OK; } break; case VM68K_AM_INDIR: if (legal&VM68K_LEGAL_AM_INDIR) { *ea=(pVM68k->a[reg])%pVM68k->memsize; retval=VM68K_OK; } break; case VM68K_AM_POSTINC: if (legal&VM68K_LEGAL_AM_POSTINC) { *ea=pVM68k->a[reg]; pNext->a[reg]+=bytesize; retval=VM68K_OK; } break; case VM68K_AM_PREDEC: if (legal&VM68K_LEGAL_AM_PREDEC) { pNext->a[reg]-=bytesize; *ea=pNext->a[reg]; retval=VM68K_OK; } break; case VM68K_AM_DISP16: if (legal&VM68K_LEGAL_AM_DISP16) { *ea=(tVM68k_sword)READEXTENSIONWORD(pVM68k,pNext); *ea=(*ea)+pVM68k->a[reg]; retval=VM68K_OK; } break; case VM68K_AM_INDEX: if (legal&VM68K_LEGAL_AM_INDEX) { tVM68k_uword extword; // bit 15: =0 data, =1 addr reg // bit 14..12: regnum // bit 11: =0 index register is a signed word // =1 index register is a signed long // bit 10..8: UNKNOWN // bit 7..0: displacement, signed byte tVM68k_ubyte regX; tVM68k_sbyte displacement1; tVM68k_slong displacement2l; tVM68k_sword displacement2w; extword=(tVM68k_uword)READEXTENSIONWORD(pVM68k,pNext); regX=(extword>>12)&0x7; displacement1=(extword&0xff); if ((extword>>15)&1) { displacement2l=pVM68k->a[regX]; displacement2w=(pVM68k->a[regX]&0xffff); } else { displacement2l=pVM68k->d[regX]; displacement2w=(pVM68k->d[regX]&0xffff); } *ea=displacement1+(((extword>>11)&1)?displacement2l:displacement2w); *ea=(*ea)+pVM68k->a[reg]; retval=VM68K_OK; } break; default: retval=VM68K_NOK_INVALID_PTR;break; } } return retval; } // the way addresses are stored here is that memory addresses are >=0. <=0 addresses the registers. int vm68k_fetchoperand(tVM68k* pVM68k,tVM68k_bool extendsign,tVM68k_types size,tVM68k_slong ea,tVM68k_ulong* operand) { int retval; tVM68k_ulong op; op=0; if (ea>=0) // memory address { ea%=pVM68k->memsize; // just to be safe... retval=VM68K_OK; switch (size) { case VM68K_BYTE: op= READ_INT8BE(pVM68k->pMem,ea);break; case VM68K_WORD: op=READ_INT16BE(pVM68k->pMem,ea);break; case VM68K_LONG: op=READ_INT32BE(pVM68k->pMem,ea);break; default: retval=VM68K_NOK_INVALID_PTR;break; } } else { // register address if (ea>=DATAREGADDR(7) && ea<=DATAREGADDR(0)) { op=pVM68k->d[-ea+DATAREGADDR(0)]; retval=VM68K_OK; } else if (ea>=ADDRREGADDR(7) && ea<=ADDRREGADDR(0)) { op=pVM68k->a[-ea+ADDRREGADDR(0)]; retval=VM68K_OK; } else retval=VM68K_NOK_UNKNOWN_INSTRUCTION; } switch (size) { case VM68K_BYTE: op&= 0xff;if (extendsign) op=(tVM68k_slong)((tVM68k_sbyte)op);break; case VM68K_WORD: op&=0xffff;if (extendsign) op=(tVM68k_slong)((tVM68k_sword)op);break; default: break; } *operand=op; return retval; } int vm68k_calculateflags(tVM68k_next* pNext,tVM68k_ubyte flagmask,tVM68k_types size,tVM68k_ulong operand1,tVM68k_ulong operand2,tVM68k_uint64 result) { tVM68k_ubyte msb; tVM68k_ulong mask; tVM68k_sint64 maxval,minval; tVM68k_sint64 res; int retval; retval=VM68K_OK; mask=0; msb=0; maxval=0; minval=0; res=0; switch (size) { case VM68K_BYTE: msb= 8;mask= 0xff;maxval= 0x7fll;minval= -0x80ll;res=((tVM68k_sint64)((tVM68k_sbyte)(result& 0xff)));break; case VM68K_WORD: msb=16;mask= 0xffff;maxval= 0x7fffll;minval= -0x8000ll;res=((tVM68k_sint64)((tVM68k_sbyte)(result& 0xffff)));break; case VM68K_LONG: msb=32;mask=0xffffffff;maxval=0x7fffffffll;minval=-0x80000000ll;res=((tVM68k_sint64)((tVM68k_sbyte)(result&0xffffffff)));break; default: retval=VM68K_NOK_INVALID_PTR;break; } if (flagmask&FLAGC) pNext->cflag=((operand2^result)>>msb)&1; if (flagmask&FLAGZ) pNext->zflag=((result&mask)==0); if (flagmask&FLAGN) pNext->nflag=(result>>(msb-1))&1; // if (flagmask&FLAGV) pNext->vflag=((operand1^operand2^result)>>(msb-1))&1; //if (flagmask&FLAGV) pNext->vflag=((~(operand1^operand2)^result)>>(msb-1))&1; if (flagmask&FLAGV) pNext->vflag=((res>maxval)||(resxflag=pNext->cflag; if (flagmask&FLAGCZCLR) {pNext->cflag=0;pNext->vflag=0;} return retval; } int vm68k_calculateflags2(tVM68k_next* pNext,tVM68k_ubyte flagmask,tVM68k_instruction instruction,tVM68k_types datatype,tVM68k_ulong operand1,tVM68k_ulong operand2,tVM68k_uint64 result) { tVM68k_bool msb1,msb2,msbres; int retval=VM68K_OK; msb1=msb2=msbres=0; switch(datatype) { case VM68K_BYTE: msb1=(operand1>> 7)&1;msb2=(operand2>> 7)&1;msbres=(result>> 7)&1;break; case VM68K_WORD: msb1=(operand1>>15)&1;msb2=(operand2>>15)&1;msbres=(result>>15)&1;break; case VM68K_LONG: msb1=(operand1>>31)&1;msb2=(operand2>>31)&1;msbres=(result>>31)&1;break; default: retval=VM68K_NOK_INVALID_PTR;break; } pNext->zflag=(result==0); pNext->nflag=(msbres); switch (instruction) { case VM68K_INST_ADD: case VM68K_INST_ADDA: case VM68K_INST_ADDI: case VM68K_INST_ADDQ: case VM68K_INST_ADDX: // sr[0] <= (`Sm & `Dm) | (~`Rm & `Dm) | (`Sm & ~`Rm); // sr[1] <= (`Sm & `Dm & ~`Rm) | (~`Sm & ~`Dm & `Rm); pNext->cflag=(msb1&msb2)|((!msbres)&msb2)|(msb1&(!msbres)); pNext->xflag=pNext->cflag; pNext->vflag=(msb1&msb2&(!msbres))|((!msb1)&(!msb2)&msbres); break; case VM68K_INST_SUB: case VM68K_INST_SUBA: case VM68K_INST_SUBI: case VM68K_INST_SUBQ: case VM68K_INST_SUBX: // sr[0] <= (`Sm & ~`Dm) | (`Rm & ~`Dm) | (`Sm & `Rm); // sr[1] <= (~`Sm & `Dm & ~`Rm) | (`Sm & ~`Dm & `Rm); pNext->cflag=(msb1&(!msb2))|(msbres&(!msb2))|(msb1&msbres); pNext->xflag=pNext->cflag; pNext->vflag=((!msb1)&msb2&(!msbres))|(msb1&(!msb2)&msbres); break; case VM68K_INST_CMP: case VM68K_INST_CMPA: case VM68K_INST_CMPI: case VM68K_INST_CMPM: // sr[0] <= (`Sm & ~`Dm) | (`Rm & ~`Dm) | (`Sm & `Rm); // sr[1] <= (~`Sm & `Dm & ~`Rm) | (`Sm & ~`Dm & `Rm); pNext->cflag=(msb1&(!msb2))|(msbres&(!msb2))|(msb1&msbres); pNext->vflag=((!msb1)&msb2&(!msbres))|(msb1&(!msb2)&msbres); break; case VM68K_INST_AND: case VM68K_INST_EOR: case VM68K_INST_OR: case VM68K_INST_ANDI: case VM68K_INST_EORI: case VM68K_INST_ORI: default: pNext->cflag=0; pNext->vflag=0; break; } return retval; } int vm68k_storeresult(tVM68k* pVM68k,tVM68k_next* pNext,tVM68k_types size,tVM68k_slong ea,tVM68k_ulong result) { int retval; tVM68k_ulong uppermask; tVM68k_ulong lowermask; retval=VM68K_NOK_UNKNOWN_INSTRUCTION; switch (size) { case VM68K_BYTE:uppermask=0xffffff00;lowermask=~uppermask;break; case VM68K_WORD:uppermask=0xffff0000;lowermask=~uppermask;break; case VM68K_LONG:uppermask=0x00000000;lowermask=~uppermask;break; default: return 0; } if (ea>=0) // memory address { retval=VM68K_OK; ea%=pVM68k->memsize; // just to be safe... pNext->mem_size=size; pNext->mem_addr[pNext->mem_we]=ea; pNext->mem_value[pNext->mem_we]=result&lowermask; pNext->mem_we++; } else { // register address if (ea>=DATAREGADDR(7) && ea<=DATAREGADDR(0)) { int reg; reg=-ea+DATAREGADDR(0); pNext->d[reg]&=uppermask; pNext->d[reg]|=(result&lowermask); retval=VM68K_OK; } else if (ea>=ADDRREGADDR(7) && ea<=ADDRREGADDR(0)) { int reg; reg=-ea+ADDRREGADDR(0); pNext->a[reg]&=uppermask; pNext->a[reg]|=(result&lowermask); retval=VM68K_OK; } else retval=VM68K_NOK_UNKNOWN_INSTRUCTION; } return retval; } dmagnetic-0.32/src/engine/vm68k/vm68k_loadstore.h0000644000175000017500000001142414076357617021224 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef VM68K_LOADSTORE_H #define VM68K_LOADSTORE_H #include "vm68k_datatypes.h" // some helper defines #define DATAREGADDR(addr) (-((addr)+ 1)) #define ADDRREGADDR(addr) (-((addr)+10)) #define VM68K_LEGAL_AM_DATAREG (1<< 1) #define VM68K_LEGAL_AM_ADDRREG (1<< 2) #define VM68K_LEGAL_AM_INDIR (1<< 3) #define VM68K_LEGAL_AM_POSTINC (1<< 4) #define VM68K_LEGAL_AM_PREDEC (1<< 5) #define VM68K_LEGAL_AM_DISP16 (1<< 6) #define VM68K_LEGAL_AM_INDEX (1<< 7) #define VM68K_LEGAL_AMX_W (1<< 8) #define VM68K_LEGAL_AMX_L (1<< 9) #define VM68K_LEGAL_AMX_DATA (1<<10) #define VM68K_LEGAL_AMX_PC (1<<11) #define VM68K_LEGAL_AMX_INDEX_PC (1<<12) #define VM68K_LEGAL_CONTROLALTERATEADDRESSING (VM68K_LEGAL_AM_INDIR|VM68K_LEGAL_AM_DISP16|VM68K_LEGAL_AM_INDEX|VM68K_LEGAL_AMX_W|VM68K_LEGAL_AMX_L|VM68K_LEGAL_AMX_PC|VM68K_LEGAL_AMX_INDEX_PC) #define VM68K_LEGAL_CONTROLADDRESSING (VM68K_LEGAL_AM_INDIR|VM68K_LEGAL_AM_DISP16|VM68K_LEGAL_AM_INDEX|VM68K_LEGAL_AMX_W|VM68K_LEGAL_AMX_L|VM68K_LEGAL_AMX_PC|VM68K_LEGAL_AMX_INDEX_PC) #define VM68K_LEGAL_DATAADDRESSING ( VM68K_LEGAL_AM_DATAREG| VM68K_LEGAL_AM_INDIR| VM68K_LEGAL_AM_POSTINC| VM68K_LEGAL_AM_PREDEC| VM68K_LEGAL_AM_DISP16| VM68K_LEGAL_AM_INDEX| VM68K_LEGAL_AMX_W| VM68K_LEGAL_AMX_L| VM68K_LEGAL_AMX_DATA| VM68K_LEGAL_AMX_PC| VM68K_LEGAL_AMX_INDEX_PC ) #define VM68K_LEGAL_ALL ( VM68K_LEGAL_AM_DATAREG| VM68K_LEGAL_AM_ADDRREG| VM68K_LEGAL_AM_INDIR| VM68K_LEGAL_AM_POSTINC| VM68K_LEGAL_AM_PREDEC| VM68K_LEGAL_AM_DISP16| VM68K_LEGAL_AM_INDEX| VM68K_LEGAL_AMX_W| VM68K_LEGAL_AMX_L| VM68K_LEGAL_AMX_DATA| VM68K_LEGAL_AMX_PC| VM68K_LEGAL_AMX_INDEX_PC ) #define VM68K_LEGAL_DATAALTERATE ( VM68K_LEGAL_AM_DATAREG|VM68K_LEGAL_AM_INDIR|VM68K_LEGAL_AM_POSTINC| VM68K_LEGAL_AM_PREDEC| VM68K_LEGAL_AM_DISP16| VM68K_LEGAL_AM_INDEX| VM68K_LEGAL_AMX_W| VM68K_LEGAL_AMX_L) #define VM68K_LEGAL_MEMORYALTERATE ( VM68K_LEGAL_AM_INDIR|VM68K_LEGAL_AM_POSTINC| VM68K_LEGAL_AM_PREDEC| VM68K_LEGAL_AM_DISP16| VM68K_LEGAL_AM_INDEX| VM68K_LEGAL_AMX_W| VM68K_LEGAL_AMX_L) #define VM68K_LEGAL_ALTERABLEADRESSING (VM68K_LEGAL_AM_DATAREG|VM68K_LEGAL_AM_ADDRREG|VM68K_LEGAL_AM_INDIR|VM68K_LEGAL_AM_POSTINC|VM68K_LEGAL_AM_PREDEC|VM68K_LEGAL_AM_DISP16|VM68K_LEGAL_AM_INDEX| VM68K_LEGAL_AMX_W|VM68K_LEGAL_AMX_L) #define FLAGC (1<<0) #define FLAGV (1<<1) #define FLAGZ (1<<2) #define FLAGN (1<<3) #define FLAGX (1<<4) #define FLAGCZCLR (1<<5) // when set, the c and z flag are always cleared. #define FLAGS_ALL (FLAGC|FLAGV|FLAGZ|FLAGN|FLAGX) #define FLAGS_LOGIC (FLAGZ|FLAGN|FLAGCZCLR) // this function is calculating the number of bytes for a datatype int vm68k_getbytesize(tVM68k_types size); // this function is translating the addressmode/registerpair into a memory address int vm68k_resolve_ea(tVM68k* pVM68k,tVM68k_next *pNext,tVM68k_types size, tVM68k_addrmodes addrmode,tVM68k_ubyte reg, tVM68k_uword legal,tVM68k_slong* ea); // this function is loading the operand int vm68k_fetchoperand(tVM68k* pVM68k,tVM68k_bool extendsign,tVM68k_types size,tVM68k_slong ea,tVM68k_ulong* operand); // this function is calculting the status flags int vm68k_calculateflags(tVM68k_next* pNext,tVM68k_ubyte flagmask,tVM68k_types size,tVM68k_ulong operand1,tVM68k_ulong operand2,tVM68k_uint64 result); int vm68k_calculateflags2(tVM68k_next* pNext,tVM68k_ubyte flagmask,tVM68k_instruction instruction,tVM68k_types datatype,tVM68k_ulong operand1,tVM68k_ulong operand2,tVM68k_uint64 result); // this function is storing the result of the operation. int vm68k_storeresult(tVM68k* pVM68k,tVM68k_next* pNext,tVM68k_types size,tVM68k_slong ea,tVM68k_ulong result); #endif dmagnetic-0.32/src/engine/vm68k/vm68k_datatypes.h0000644000175000017500000000770714076357617021237 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef VM68K_DATATYPES_H #define VM68K_DATATYPES_H // the purpose of this file is to provide the shared datatypes needed for the virtual machine #ifdef __sgi__ typedef unsigned char tVM68k_bool; typedef unsigned char tVM68k_ubyte; typedef unsigned short tVM68k_uword; typedef unsigned int tVM68k_ulong; typedef unsigned long long tVM68k_uint64; typedef signed char tVM68k_sbyte; typedef signed short tVM68k_sword; typedef signed int tVM68k_slong; typedef signed long long tVM68k_sint64; #else #include // first of all: the standard data types. typedef uint_least8_t tVM68k_bool; typedef uint_least8_t tVM68k_ubyte; typedef uint_least16_t tVM68k_uword; typedef uint_least32_t tVM68k_ulong; typedef uint_least64_t tVM68k_uint64; typedef int_least8_t tVM68k_sbyte; typedef int_least16_t tVM68k_sword; typedef int_least32_t tVM68k_slong; typedef int_least64_t tVM68k_sint64; #endif // then a couple of enumerations. to make the sourcecode a little bit easier to read. typedef enum _tVM68k_types {VM68K_BYTE=0,VM68K_WORD=1,VM68K_LONG=2,VM68K_UNKNOWN=3} tVM68k_types; typedef enum _tVM68k_addrmodes { VM68K_AM_DATAREG=0, // Dn VM68K_AM_ADDRREG=1, // An VM68K_AM_INDIR=2, // (An) VM68K_AM_POSTINC=3, // (An)+ VM68K_AM_PREDEC=4, // -(An) VM68K_AM_DISP16=5, // (d16,An) VM68K_AM_INDEX=6, // (d8,An,Xn) VM68K_AM_EXT=7} tVM68k_addrmodes; typedef enum _tVM68k_addrmode_ext { VM68K_AMX_W=0, // (xxx),W VM68K_AMX_L=1, // (xxx),L VM68K_AMX_data=4, // # VM68K_AMX_PC=2, // (d16,PC) VM68K_AMX_INDEX_PC=3} // (d8,PC,Xn) tVM68k_addrmode_ext; // the internal structures typedef struct _tVM68k { tVM68k_ulong magic; // just so that the functions can identify a handle as this particular data structure tVM68k_ulong pcr; // program counter tVM68k_uword sr; // status register. // bit 0..4: CVZNX tVM68k_ulong a[8]; // address register tVM68k_ulong d[8]; // data register tVM68k_ubyte *pMem; // pointer to the memory tVM68k_ulong memsize; // TODO: check for violations. /////// VERSION PATCH tVM68k_ubyte version; } tVM68k; ////// this structure holds the state after the instruction has been decoded. ////// the reason i put it in here is to trace the changes. typedef struct _tVM68k_next { tVM68k_ulong pcr; // program counter tVM68k_bool override_sr; tVM68k_uword sr; tVM68k_bool cflag; tVM68k_bool vflag; tVM68k_bool zflag; tVM68k_bool nflag; tVM68k_bool xflag; // bit 0..4: CVZNX tVM68k_ulong a[8]; // address register tVM68k_ulong d[8]; // data register ////// memory queue tVM68k_types mem_size; tVM68k_ulong mem_addr[16]; tVM68k_ulong mem_value[16]; tVM68k_ubyte mem_we; } tVM68k_next; #endif dmagnetic-0.32/src/engine/vm68k/vm68k_decode.h0000644000175000017500000001255214076357617020456 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef VM68K_DECODE_H #define VM68K_DECODE_H //#define DEBUG_PRINT #include "vm68k_datatypes.h" // this data structure makes the decoding process easier to read. typedef enum _tVM68k_instruction { VM68K_INST_UNKNOWN=0, VM68K_INST_ABCD, //1100xxx10000myyy VM68K_INST_ADD, //1101 rrro oomm myyy VM68K_INST_ADDA, //1101 rrro oomm myyy VM68K_INST_ADDI, //0000 0110 ssmm myyy VM68K_INST_ADDQ, //0101 ddd0 ssmm myyy VM68K_INST_ADDX, //1101 xxx1 ss00 myyy VM68K_INST_AND, //1100 xxxo oomm myyy VM68K_INST_ANDI, //0000 0010 ssmm myyy VM68K_INST_ANDItoCCR, //0000 0010 0011 1100 00000000dddddddd VM68K_INST_ANDItoSR, //0000 0010 0111 1100 dddddddddddddddd VM68K_INST_ASL_ASR, //1110 cccd ssl0 0yyy VM68K_INST_BCC, //0110 CCCC dddd dddd VM68K_INST_BCHG, //0000 xxx1 01mm myyy VM68K_INST_BCHGB, //0000 1000 10mm myyy VM68K_INST_BCLR, //0000 xxx1 10mm myyy VM68K_INST_BCLRI, //0000xxx110mmmyyy VM68K_INST_BRA, //0110 0000 dddd dddd VM68K_INST_BSET, //0000 xxx1 11mm myyy VM68K_INST_BSETB, //0000 1000 11mm myyy // VM68K_INST_BSR, //01100001dddddddd VM68K_INST_BTST, //0000 xxx1 00mm myyy VM68K_INST_BTSTB, //0000 1000 00mm myyy VM68K_INST_CHK, //0100xxxss0mmmyyy VM68K_INST_CLR, //0100 0010 ssmm myyy VM68K_INST_CMP, //1011 xxxo oomm myyy VM68K_INST_CMPA, //1011 xxxo oomm myyy VM68K_INST_CMPI, //0000 1100 ssmm myyy VM68K_INST_CMPM, //1011 xxx1 ss00 1yyy VM68K_INST_DBcc, //0101 CCCC 1100 1yyy VM68K_INST_DIVS, //1000xxx111mmmyyy VM68K_INST_DIVU, //1000xxx011mmmyyy VM68K_INST_EOR, //1011 xxxo oomm myyy VM68K_INST_EORI, //0000 1010 ssmm myyy VM68K_INST_EORItoCCR, //0000 1010 0011 1100 00000000dddddddd VM68K_INST_EORItoSR, //0000 1010 0111 1100 dddddddddddddddd VM68K_INST_EXG, //1100 xxx1 oooo oyyy VM68K_INST_EXT, //0100 100o oo00 0yyy VM68K_INST_ILLEGAL, //0100101011111100 VM68K_INST_JMP, //0100 1110 11mm myyy VM68K_INST_JSR, //0100 1110 10mm myyy VM68K_INST_LEA, //0100 xxx1 11mm myyy VM68K_INST_LINK, //0100111001010yyydddddddddddddddd VM68K_INST_LSL_LSR, //1110 cccd ssl0 1yyy VM68K_INST_MOVE, //00ss xxxm mmMM Myyy VM68K_INST_MOVEA, //00ss xxx0 01mm myyy VM68K_INST_MOVEtoCCR, //0100010011mmmyyy VM68K_INST_MOVEfromSR, //0100000011mmmyyy VM68K_INST_MOVEtoSR, //0100011011mmmyyy VM68K_INST_MOVEUSP, //010011100110dyyy VM68K_INST_MOVEMregtomem, //0100 1d00 1smm myyy VM68K_INST_MOVEMmemtoreg, //0100 1d00 1smm myyy VM68K_INST_MOVEP, //0000xxxooo001yyydddddddddddddddd VM68K_INST_MOVEQ, //0111xxx0dddddddd VM68K_INST_MULS, //1100xxx111mmmyyy VM68K_INST_MULU, //1100xxx011mmmyyy!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VM68K_INST_NBCD, //1100xxx011mmmyyy!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VM68K_INST_NEG, //0100 0100 ssmm myyy VM68K_INST_NEGX, //0100 0000 ssmm myyy VM68K_INST_NOP, //0100 1110 0111 0001 VM68K_INST_NOT, //0100 0110 ssmm myyy VM68K_INST_OR, //1000 xxxo oomm myyy VM68K_INST_ORI, //0000 0000 ssmm myyy VM68K_INST_ORItoCCR, //0000 0000 0011 1100 00000000dddddddd VM68K_INST_ORItoSR, //0000 0000 0111 1100 dddddddddddddddd VM68K_INST_PEA, //0100 1000 01mm myyy VM68K_INST_RESET, //0100111001110000 VM68K_INST_ROL_ROR, //1110 cccd ssl1 1yyy VM68K_INST_ROXL_ROXR, //1110 cccd ssl1 0yyy VM68K_INST_RTE, //0100 1110 0111 0011 VM68K_INST_RTR, //0100 1110 0111 0111 VM68K_INST_RTS, //0100 1110 0111 0101 VM68K_INST_SBCD, //1000xxx10000ryyy VM68K_INST_SCC, //0101 CCCC 11mm myyy VM68K_INST_STOP, //0100111001110010iiiiiiiiiiiiiiii VM68K_INST_SUB, //1001 xxxo oomm myyy VM68K_INST_SUBA, //1001 xxxo oomm myyy VM68K_INST_SUBI, //0000 0100 ssmm myyy VM68K_INST_SUBQ, //0101 ddd1 ssmm myyy VM68K_INST_SUBX, //1001 yyy1 ss00 ryyy VM68K_INST_SWAP, //0100100001000yyy VM68K_INST_TAS, //0100101011mmmyyy VM68K_INST_TRAP, //010011100100vvvv VM68K_INST_TRAPV, //0100111001110110 VM68K_INST_TST, //0100 1010 ssmm myyy VM68K_INST_UNLK, //0100111001011yyy } tVM68k_instruction; // opcodes are 16 bit values, this function translates them into an easier-to-handle enumeration. tVM68k_instruction vm68k_decode(tVM68k_uword opcode); #ifdef DEBUG_PRINT // this function is for translating the enumeration into something human-readable. void vm68k_get_instructionname(tVM68k_instruction instruction,char* name); #endif #endif dmagnetic-0.32/src/engine/vm68k/vm68k.c0000644000175000017500000010052714076357617017146 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "vm68k_datatypes.h" #include "vm68k_decode.h" #include "vm68k_loadstore.h" #include "vm68k_macros.h" #include "vm68k.h" #define MAGICVALUE 0x564A3638 // "VM68" tVM68k_bool vm68k_checkcondition(tVM68k* pVM68k,tVM68k_ubyte condition) { #define CFLAG(pVM68k) ((((pVM68k)->sr)>>0)&1) #define VFLAG(pVM68k) ((((pVM68k)->sr)>>1)&1) #define ZFLAG(pVM68k) ((((pVM68k)->sr)>>2)&1) #define NFLAG(pVM68k) ((((pVM68k)->sr)>>3)&1) #define XFLAG(pVM68k) ((((pVM68k)->sr)>>4)&1) tVM68k_bool condtrue; switch(condition) { case 0: condtrue=1;break; case 1: condtrue=0;break; case 2: //BHI high 0010 /C | /Z condtrue=!(CFLAG(pVM68k)|ZFLAG(pVM68k)); break; case 3: //LS low or same 0011 C & Z condtrue= (CFLAG(pVM68k)|ZFLAG(pVM68k)); break; case 4://BCC carry clear 0100 /C condtrue=!(CFLAG(pVM68k)); break; case 5://BCS carry set 0101 C condtrue= (CFLAG(pVM68k)); break; case 6://BNE not equal 0110 /Z condtrue=!(ZFLAG(pVM68k)); break; case 7://BEQ equal 0111 Z condtrue= (ZFLAG(pVM68k)); break; case 8://BVC overflow clear 1000 /V condtrue=!(VFLAG(pVM68k)); break; case 9://BVS overflow set 1001 V condtrue= (VFLAG(pVM68k)); break; case 10://BPL plus 1010 /N condtrue=!(NFLAG(pVM68k)); break; case 11://BMI minus 1011 N condtrue= (NFLAG(pVM68k)); break; case 12://BGE greater or equal 1100 (N & V) | (/N & /V), N and V are both set or both clear condtrue=!((NFLAG(pVM68k)^VFLAG(pVM68k))); break; case 13://BLT less than 1101 (N & /V) | (/N & V), N and V are either set or clear condtrue= ((NFLAG(pVM68k)^VFLAG(pVM68k))); break; case 14://BGT greater than 1110 (N & V & /Z) | (/N & /V & /Z) condtrue=!ZFLAG(pVM68k)&!((NFLAG(pVM68k)^VFLAG(pVM68k))); break; case 15://BLE less or equal 1111 condtrue=ZFLAG(pVM68k)|((NFLAG(pVM68k)^VFLAG(pVM68k))); break; default: condtrue=0; break; } return condtrue; } int vm68k_getsize(int* size) { if (size==NULL) return VM68K_NOK_INVALID_PTR; *size=sizeof(tVM68k); return VM68K_OK; } int vm68k_init(void* hVM68k,void* pSharedMem,int sharedmemsize,int version) { tVM68k* pVM68k=(tVM68k*)hVM68k; if (hVM68k==NULL) return VM68K_NOK_INVALID_PTR; if (pSharedMem==NULL) return VM68K_NOK_INVALID_PARAMETER; memset(hVM68k,0,sizeof(tVM68k)); pVM68k->magic=MAGICVALUE; pVM68k->pcr=0; pVM68k->pMem=pSharedMem; pVM68k->memsize=sharedmemsize; pVM68k->a[7]=pVM68k->memsize-4; // The stack pointer goes to the end of the memory pVM68k->version=version; return VM68K_OK; } int vm68k_singlestep(void *hVM68k,unsigned short opcode) { tVM68k* pVM68k=(tVM68k*)hVM68k; tVM68k_instruction instruction; tVM68k_ubyte addrmode; tVM68k_ubyte reg1,reg2; tVM68k_types datatype; tVM68k_next next; tVM68k_slong ea; tVM68k_ulong operand1,operand2; tVM68k_uint64 result; tVM68k_ubyte condition; tVM68k_sword displacement; tVM68k_bool direction; int retval; int i; if (hVM68k==NULL) return VM68K_NOK_INVALID_PTR; if (pVM68k->magic!=MAGICVALUE) return VM68K_NOK_INVALID_PARAMETER; retval=VM68K_NOK_UNKNOWN_INSTRUCTION; instruction=vm68k_decode(opcode); // decode the opcode reg1=(opcode>>9)&0x7; addrmode=(opcode>>3)&0x7; reg2=(opcode>>0)&0x7; datatype=(tVM68k_types)(opcode>>6)&0x3; // branches condition=(opcode>>8)&0xf; displacement=(tVM68k_sword)((tVM68k_sbyte)(opcode&0xff)); // alu operations direction=(opcode>>8)&0x1; INITNEXT(pVM68k,next); result=0; switch(instruction) { case VM68K_INST_TRAP: printf("\x1b[1;37;42mtrap #%d\n",opcode&0xf); for (i=0;i<16;i++) { printf(" ** trap %d stack %2d %08X \n",opcode&0xf,i,READ_INT32BE(pVM68k->pMem,pVM68k->a[7]-i*4)); } printf("\x1b[0m\n"); retval=VM68K_OK; break; case VM68K_INST_MULU: retval=vm68k_resolve_ea(pVM68k,&next,VM68K_WORD,addrmode,reg2,VM68K_LEGAL_ALL,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,VM68K_WORD,ea,&operand1); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,VM68K_WORD,DATAREGADDR(reg1),&operand2); if (retval==VM68K_OK) result=((unsigned int)operand1&0xffff)*((unsigned short)operand2&0xffff); if (retval==VM68K_OK) retval=vm68k_calculateflags2(&next,FLAGS_ALL,instruction,VM68K_LONG,operand1,operand2,result); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,VM68K_LONG,DATAREGADDR(reg1),result); break; case VM68K_INST_DIVU: // FIXME: division by 0? retval=vm68k_resolve_ea(pVM68k,&next,VM68K_WORD,addrmode,reg2,VM68K_LEGAL_ALL,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,VM68K_WORD,ea,&operand1); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,VM68K_WORD,DATAREGADDR(reg1),&operand2); // upper 16 bits are the remainder if (retval==VM68K_OK) result=(((unsigned int)operand1&0xffff)%((unsigned short)operand2&0xffff))<<16; // lower 16 bits are the quotient if (retval==VM68K_OK) result|=(((unsigned int)operand1&0xffff)/((unsigned short)operand2&0xffff))&0xffff; if (retval==VM68K_OK) retval=vm68k_calculateflags2(&next,FLAGS_ALL,instruction,VM68K_LONG,operand1,operand2,result); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,VM68K_LONG,DATAREGADDR(reg1),result); break; case VM68K_INST_ADD: case VM68K_INST_CMP: case VM68K_INST_SUB: if (instruction==VM68K_INST_CMP) { retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,VM68K_LEGAL_ALL,&ea); } else { retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,direction?VM68K_LEGAL_MEMORYALTERATE:VM68K_LEGAL_ALL,&ea); } if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,1,datatype,ea,&operand1); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,1,datatype,DATAREGADDR(reg1),&operand2); if (instruction==VM68K_INST_SUB || instruction==VM68K_INST_CMP) { if (direction) { result=operand1-operand2; } else { result=operand2-operand1; } } else { result=operand2+operand1; } if (retval==VM68K_OK) retval=vm68k_calculateflags2(&next,FLAGS_ALL,instruction,datatype,operand1,operand2,result); if (retval==VM68K_OK && instruction!=VM68K_INST_CMP) retval=vm68k_storeresult(pVM68k,&next,datatype,(direction)?ea:DATAREGADDR(reg1),result); break; case VM68K_INST_ADDA: case VM68K_INST_CMPA: case VM68K_INST_SUBA: if (datatype==VM68K_UNKNOWN) { tVM68k_types datatype2; tVM68k_types datatype3; datatype2=((opcode>>8)&1)?VM68K_LONG:VM68K_WORD; if (pVM68k->version==4) { datatype3=VM68K_LONG; } else { datatype3=datatype2; } retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_ALL,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,1,datatype2,ea,&operand2); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,1,datatype3,ADDRREGADDR(reg1),&operand1); if (instruction==VM68K_INST_SUBA || instruction==VM68K_INST_CMPA) { result=operand1-operand2; } else { result=operand2+operand1; } if (retval==VM68K_OK && instruction==VM68K_INST_CMPA) retval=vm68k_calculateflags2(&next,FLAGS_ALL,instruction,datatype2,operand1,operand2,result); if (retval==VM68K_OK && instruction!=VM68K_INST_CMPA) retval=vm68k_storeresult(pVM68k,&next,datatype3,ADDRREGADDR(reg1),result); } break; case VM68K_INST_ADDI: case VM68K_INST_CMPI: case VM68K_INST_SUBI: READEXTENSION(pVM68k,&next,datatype,operand1); retval=VM68K_OK; switch(datatype) { case VM68K_BYTE: operand1=(tVM68k_slong)((tVM68k_sbyte)(operand1& 0xff));break; case VM68K_WORD: operand1=(tVM68k_slong)((tVM68k_sword)(operand1& 0xffff));break; case VM68K_LONG: operand1=(tVM68k_slong)((tVM68k_slong)(operand1&0xffffffff));break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION;break; } if (retval==VM68K_OK) retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,VM68K_LEGAL_DATAALTERATE,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,1,datatype,ea,&operand2); if (instruction==VM68K_INST_SUBI || instruction==VM68K_INST_CMPI) { result=operand2-operand1; // Checked 0c01 } else { result=operand2+operand1; } if (retval==VM68K_OK) retval=vm68k_calculateflags2(&next,FLAGS_ALL,instruction,datatype,operand1,operand2,result); if (retval==VM68K_OK && instruction!=VM68K_INST_CMPI) retval=vm68k_storeresult(pVM68k,&next,datatype,ea,result); break; case VM68K_INST_ADDQ: case VM68K_INST_SUBQ: { tVM68k_sbyte quick; tVM68k_bool version3_workaround; retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,VM68K_LEGAL_ALTERABLEADRESSING,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,1,datatype,ea,&operand2); quick=reg1; operand1=quick; if (operand1==0) operand1=8; if (instruction==VM68K_INST_SUBQ) { result=operand2-operand1; } else { result=operand2+operand1; } version3_workaround=next.zflag; // starting with version 3, the z-flag needed to be preserved. this was an inconsistency in the original engine, that just stuck. if (retval==VM68K_OK) retval=vm68k_calculateflags2(&next,FLAGS_ALL,instruction,datatype,operand1,operand2,result); if ((pVM68k->version>=3) && (instruction==VM68K_INST_ADDQ)) next.zflag=version3_workaround; if (retval==VM68K_OK && instruction!=VM68K_INST_CMPI) retval=vm68k_storeresult(pVM68k,&next,datatype,ea,result); } break; case VM68K_INST_EXG: { tVM68k_sbyte opmode; opmode=(opcode>>3)&0x1f; switch(opmode) { case 8: next.d[reg1]=pVM68k->d[reg2]; next.d[reg2]=pVM68k->d[reg1];break; // 01000= data registers. case 9: next.a[reg1]=pVM68k->a[reg2]; next.a[reg2]=pVM68k->a[reg1];break; // 01001= addr registers. case 17:next.d[reg1]=pVM68k->a[reg2]; next.a[reg2]=pVM68k->d[reg1];break; // 10001= data +addr registers. } retval=VM68K_OK; } break; case VM68K_INST_MOVEQ: { tVM68k_types datatype2; tVM68k_sbyte data; datatype2=VM68K_LONG; data=opcode&0xff; result=(tVM68k_slong)data; retval=vm68k_calculateflags(&next,FLAGS_LOGIC,datatype2,0,0,result); next.cflag=next.vflag=0; if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype2,DATAREGADDR(reg1),result); } break; case VM68K_INST_AND: case VM68K_INST_EOR: case VM68K_INST_OR: // direction=1: -Dn -> // direction=0: Dn- -> Dn if (instruction==VM68K_INST_EOR) // TODO: is this really neessary? { retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,VM68K_LEGAL_ALL,&ea); } else { retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,direction?VM68K_LEGAL_MEMORYALTERATE:VM68K_LEGAL_ALL,&ea); } if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype,ea,&operand2); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype,DATAREGADDR(reg1),&operand1); switch (instruction) { case VM68K_INST_AND: result=operand1&operand2;break; case VM68K_INST_EOR: result=operand1^operand2;break; case VM68K_INST_OR: result=operand1|operand2;break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION;break; } if (retval==VM68K_OK) retval=vm68k_calculateflags(&next,FLAGS_LOGIC,datatype,operand1,operand2,result); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype,(direction)?ea:DATAREGADDR(reg1),result); break; case VM68K_INST_ANDI: case VM68K_INST_EORI: case VM68K_INST_ORI: READEXTENSION(pVM68k,&next,datatype,operand1); retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,VM68K_LEGAL_DATAALTERATE,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype,ea,&operand2); switch (instruction) { case VM68K_INST_ANDI:result=operand1&operand2;break; case VM68K_INST_EORI:result=operand1^operand2;break; case VM68K_INST_ORI: result=operand1|operand2;break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION;break; } if (retval==VM68K_OK) retval=vm68k_calculateflags(&next,FLAGS_LOGIC,datatype,operand1,operand2,result); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype,ea,result); break; case VM68K_INST_BCC: if (displacement==0) { displacement=READEXTENSIONWORD(pVM68k,&next); } if (condition==1) // BSR { PUSHLONGTOSTACK(pVM68k,&next,(next.pcr)); } if (vm68k_checkcondition(pVM68k,condition) || condition==1) { next.pcr=pVM68k->pcr+displacement; } retval=VM68K_OK; break; case VM68K_INST_MOVE: case VM68K_INST_MOVEA: { tVM68k_types datatype2; tVM68k_ubyte addrmode_dest; tVM68k_slong ea_dest; datatype2=VM68K_UNKNOWN; retval=VM68K_OK; switch ((opcode>>12)&0x3) { case 1: datatype2=VM68K_BYTE;break; case 3: datatype2=VM68K_WORD;break; case 2: datatype2=VM68K_LONG;break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION; } addrmode_dest=(opcode>>6)&0x7; if (retval==VM68K_OK) retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_ALL,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype2,ea,&operand2); // TODO: I had a problem here, when the addrmode was 7/4 and the size was BYTE. lets see what happens. if (retval==VM68K_OK) retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode_dest,reg1,(instruction==VM68K_INST_MOVE)?VM68K_LEGAL_DATAALTERATE:VM68K_LEGAL_ALL,&ea_dest); if (retval==VM68K_OK) result=operand2; if (retval==VM68K_OK && instruction!=VM68K_INST_MOVEA) retval=vm68k_calculateflags(&next,FLAGS_LOGIC,datatype2,0,operand2,result); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype2,ea_dest,result); } break; case VM68K_INST_NEG: case VM68K_INST_NEGX: case VM68K_INST_NOT: { retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,VM68K_LEGAL_DATAALTERATE,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,1,datatype,ea,&operand2); result=(instruction==VM68K_INST_NOT)?(~operand2):(0-operand2); result=result-((instruction==VM68K_INST_NEGX)&next.xflag); operand1=0; if (retval==VM68K_OK) retval=vm68k_calculateflags(&next,FLAGS_LOGIC,datatype,operand1,operand2,result); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype,ea,result); } break; case VM68K_INST_JMP: case VM68K_INST_JSR: { tVM68k_types datatype2; datatype2=VM68K_LONG; retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_CONTROLADDRESSING,&ea); operand2=ea; if (instruction==VM68K_INST_JSR && retval==VM68K_OK) { PUSHLONGTOSTACK(pVM68k,&next,(next.pcr)); } // TODO: why this way? switch (addrmode) { case VM68K_AM_INDIR: next.pcr=operand2%pVM68k->memsize; break; default: next.pcr=operand2; // wonderland break; } } break; case VM68K_INST_RTS: retval=VM68K_OK; POPLONGFROMSTACK(pVM68k,&next,next.pcr); break; case VM68K_INST_ANDItoSR: case VM68K_INST_EORItoSR: case VM68K_INST_ORItoSR: case VM68K_INST_ANDItoCCR: case VM68K_INST_EORItoCCR: case VM68K_INST_ORItoCCR: retval=VM68K_OK; operand2=READEXTENSIONWORD(pVM68k,&next); operand1=0xffff; switch (instruction) { case VM68K_INST_ANDItoCCR: operand1&=0x1f; case VM68K_INST_ANDItoSR: operand2&=next.sr;break; case VM68K_INST_EORItoCCR: operand1&=0x1f; case VM68K_INST_EORItoSR: operand2^=next.sr;break; case VM68K_INST_ORItoCCR: operand1&=0x1f; case VM68K_INST_ORItoSR: operand2|=next.sr;break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION;break; } result=operand1&operand2; next.override_sr=1; next.sr&=~operand1; next.sr|=operand2; break; case VM68K_INST_MOVEfromSR: { tVM68k_types datatype2; datatype2=VM68K_WORD; retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_DATAALTERATE,&ea); result=next.sr&0xffff; if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype2,ea,result); } break; case VM68K_INST_MOVEtoCCR: case VM68K_INST_MOVEtoSR: { tVM68k_types datatype2; datatype2=VM68K_WORD; retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_DATAADDRESSING,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype2,ea,&operand2); if (retval==VM68K_OK) { next.override_sr=1; next.sr=(instruction==VM68K_INST_MOVEtoCCR)?((next.sr&0xffe0)|(operand2&0x1f)):operand2; } } break; case VM68K_INST_MOVEMregtomem: { tVM68k_types datatype2; tVM68k_uword bitmask=0; datatype2=((opcode>>6)&1)?VM68K_LONG:VM68K_WORD; retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_CONTROLALTERATEADDRESSING|VM68K_LEGAL_AM_PREDEC,&ea); // special case: the memory decrement should only be performed when the bitmask says so { for (i=0;i<8;i++) next.a[i]=pVM68k->a[i]; } if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype2,ea,&operand2); if (retval==VM68K_OK) bitmask=READEXTENSIONWORD(pVM68k,&next); if (retval==VM68K_OK) { for (i=0;i<8;i++) { if (bitmask&1) { // FIXME: technically not the stack. if (datatype2==VM68K_WORD) PUSHWORDTOSTACK(pVM68k,&next,pVM68k->a[7-i]); if (datatype2==VM68K_LONG) PUSHLONGTOSTACK(pVM68k,&next,pVM68k->a[7-i]); } bitmask>>=1; } for (i=0;i<8;i++) { if (bitmask&1) { // FIXME: technically not the stack. if (datatype2==VM68K_WORD) PUSHWORDTOSTACK(pVM68k,&next,pVM68k->d[7-i]); if (datatype2==VM68K_LONG) PUSHLONGTOSTACK(pVM68k,&next,pVM68k->d[7-i]); } bitmask>>=1; } } } break; case VM68K_INST_MOVEMmemtoreg: { tVM68k_types datatype2; tVM68k_uword bitmask=0; datatype2=((opcode>>6)&1)?VM68K_LONG:VM68K_WORD; retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_CONTROLADDRESSING|VM68K_LEGAL_AM_POSTINC,&ea); // special case: the memory increment should only be performed when the bitmask says so { for (i=0;i<8;i++) next.a[i]=pVM68k->a[i]; } if (retval==VM68K_OK) bitmask=READEXTENSIONWORD(pVM68k,&next); if (retval==VM68K_OK) { for (i=0;i<8;i++) { if (bitmask&1) { // FIXME: not really the stack. if (datatype2==VM68K_WORD) { POPWORDFROMSTACK(pVM68k,&next,next.d[i]); } if (datatype2==VM68K_LONG) { POPLONGFROMSTACK(pVM68k,&next,next.d[i]); } } bitmask>>=1; } for (i=0;i<8;i++) { if (bitmask&1) { // FIXME: not really the stack. if (datatype2==VM68K_WORD) { POPWORDFROMSTACK(pVM68k,&next,next.a[i]); next.a[i]&=0xffff; } if (datatype2==VM68K_LONG) { POPLONGFROMSTACK(pVM68k,&next,next.a[i]); } } bitmask>>=1; } } } break; case VM68K_INST_EXT: { tVM68k_types datatype2=VM68K_UNKNOWN; switch ((opcode>>6)&0x3) { case 2: datatype2=VM68K_WORD;break; case 3: datatype2=VM68K_LONG;break; } switch (datatype2) { case VM68K_WORD: result=(tVM68k_sword)((tVM68k_sbyte)(pVM68k->d[reg2]& 0xff));retval=VM68K_OK; result=((pVM68k->d[reg2])&0xffff0000)|(((tVM68k_ulong)result)&0xffff); break; case VM68K_LONG: result=(tVM68k_slong)((tVM68k_sword)(pVM68k->d[reg2]&0xffff));retval=VM68K_OK;break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION;break; } if (retval==VM68K_OK) retval=vm68k_calculateflags(&next,FLAGS_LOGIC,datatype2,0,0,result); if (retval==VM68K_OK) next.d[reg2]=result; } break; case VM68K_INST_PEA: retval=vm68k_resolve_ea(pVM68k,&next,VM68K_LONG,addrmode,reg2,VM68K_LEGAL_CONTROLADDRESSING,&ea); result=ea; if (retval==VM68K_OK) PUSHLONGTOSTACK(pVM68k,&next,result); break; case VM68K_INST_LEA: retval=vm68k_resolve_ea(pVM68k,&next,VM68K_LONG,addrmode,reg2,VM68K_LEGAL_CONTROLADDRESSING,&ea); result=ea%(pVM68k->memsize); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,VM68K_LONG,ADDRREGADDR(reg1),result); break; case VM68K_INST_NOP: retval=VM68K_OK; break; case VM68K_INST_ASL_ASR: case VM68K_INST_LSL_LSR: case VM68K_INST_ROL_ROR: case VM68K_INST_ROXL_ROXR: { tVM68k_types datatype2; tVM68k_ubyte count; tVM68k_bool direction; tVM68k_bool msb; tVM68k_bool lsb; tVM68k_ubyte bitnum; direction=(opcode>>8)&1; // 0=right. 1=left. bitnum=8; if (datatype==VM68K_UNKNOWN) // memory shift { datatype2=VM68K_WORD; count=1; retval=vm68k_resolve_ea(pVM68k,&next,VM68K_LONG,addrmode,reg2,VM68K_LEGAL_MEMORYALTERATE,&ea); } else { datatype2=datatype; if ((opcode>>5)&1) { count=pVM68k->d[reg1]%64; } else // i/r=1 -> register. i/r=0 -> immedate { count=reg1; if (count==0) count=8; } retval=VM68K_OK; ea=DATAREGADDR(reg2); } if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype,ea,&operand2); switch (datatype2) { case VM68K_BYTE: bitnum= 8;break; case VM68K_WORD: bitnum=16;break; case VM68K_LONG: bitnum=32;break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION;break; } next.vflag=0; for (i=0;i>(bitnum-1))&1; lsb=operand2&1; if (direction) // left shift { operand2<<=1; switch (instruction) { case VM68K_INST_ASL_ASR: case VM68K_INST_LSL_LSR: lsb=0;break; case VM68K_INST_ROL_ROR: lsb=msb;break; case VM68K_INST_ROXL_ROXR: lsb=next.xflag;break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION;break; } operand2|=lsb; next.cflag=next.xflag=msb; // FIXME: reallY??? if (instruction!=VM68K_INST_ASL_ASR) next.vflag|=(prevmsb^(operand2>>(bitnum-1)))&1; // set overflow flag if the msb is changed at any time. } else { /// right shift operand2>>=1; switch (instruction) { case VM68K_INST_ASL_ASR: msb=msb&1;break; case VM68K_INST_LSL_LSR: msb=0;break; case VM68K_INST_ROL_ROR: msb=lsb;break; case VM68K_INST_ROXL_ROXR: msb=next.xflag;break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION;break; } operand2|=(msb<<(bitnum-1)); next.cflag=next.xflag=lsb; } } result=operand2; if (retval==VM68K_OK) retval=vm68k_calculateflags(&next,FLAGN|FLAGZ,datatype2,0,operand2,result); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype2,ea,result); } break; case VM68K_INST_BCLR: case VM68K_INST_BCHG: case VM68K_INST_BSET: case VM68K_INST_BTST: { tVM68k_ubyte bitnum; tVM68k_types datatype2; if (addrmode==VM68K_AM_DATAREG) { datatype2=VM68K_LONG; bitnum=32; } else { datatype2=VM68K_BYTE; bitnum=8; } retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_DATAALTERATE,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype2,ea,&operand2); if (retval==VM68K_OK) { operand1=(1<<(next.d[reg1]%bitnum)); next.zflag=((operand2&operand1)==0); switch(instruction) { case VM68K_INST_BCLR: result=operand2&~operand1;break; case VM68K_INST_BCHG: result=operand2^ operand1;break; case VM68K_INST_BSET: result=operand2| operand1;break; default: result=0; break; } } if (retval==VM68K_OK && instruction!=VM68K_INST_BTST) retval=vm68k_storeresult(pVM68k,&next,datatype2,ea,result); } break; case VM68K_INST_BCLRI: case VM68K_INST_BCHGB: case VM68K_INST_BSETB: case VM68K_INST_BTSTB: { tVM68k_uword bitnum; tVM68k_types datatype2; bitnum=READEXTENSIONWORD(pVM68k,&next); datatype2=((addrmode==VM68K_AM_DATAREG)||(addrmode==VM68K_AM_ADDRREG))?VM68K_LONG:VM68K_BYTE; retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_DATAALTERATE,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype2,ea,&operand2); if (retval==VM68K_OK) { operand1=(1<<(bitnum%32)); next.zflag=((operand2&operand1)==0); switch (instruction) { case VM68K_INST_BCLRI: result=operand2&~operand1;break; case VM68K_INST_BCHGB: result=operand2^ operand1;break; case VM68K_INST_BSETB: result=operand2| operand1;break; default: result=0; break; } } if (retval==VM68K_OK && instruction!=VM68K_INST_BTSTB) retval=vm68k_storeresult(pVM68k,&next,datatype2,ea,result); } break; case VM68K_INST_ADDX: case VM68K_INST_SUBX: { tVM68k_slong ea_dest; tVM68k_bool rm; rm=(opcode>>3)&1; retval=vm68k_resolve_ea(pVM68k,&next,datatype,(rm?VM68K_AM_PREDEC:VM68K_AM_DATAREG),reg2,VM68K_LEGAL_AM_PREDEC|VM68K_LEGAL_AM_DATAREG,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype,ea,&operand2); if (retval==VM68K_OK) retval=vm68k_resolve_ea(pVM68k,&next,datatype,(rm?VM68K_AM_PREDEC:VM68K_AM_DATAREG),reg1,VM68K_LEGAL_AM_PREDEC|VM68K_LEGAL_AM_DATAREG,&ea_dest); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype,ea,&operand1); if (retval==VM68K_OK) operand1+=next.xflag; if (retval==VM68K_OK) if (instruction==VM68K_INST_SUBX) operand1=-operand1; if (retval==VM68K_OK) result=operand1+operand2; if (retval==VM68K_OK) if (result!=0) next.zflag=0; // special case if (retval==VM68K_OK) retval=vm68k_calculateflags(&next,FLAGS_ALL^FLAGZ,datatype,operand1,operand2,result); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype,ea,result); } break; case VM68K_INST_CMPM: { tVM68k_slong ea_dest; retval=vm68k_resolve_ea(pVM68k,&next,datatype,VM68K_AM_POSTINC,reg2,VM68K_LEGAL_AM_POSTINC,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,1,datatype,ea,&operand2); if (retval==VM68K_OK) retval=vm68k_resolve_ea(pVM68k,&next,datatype,VM68K_AM_POSTINC,reg1,VM68K_LEGAL_AM_POSTINC,&ea_dest); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,1,datatype,ea_dest,&operand1); if (retval==VM68K_OK) result=operand2-operand1; if (retval==VM68K_OK) retval=vm68k_calculateflags2(&next,FLAGS_ALL,instruction,datatype,operand1,operand2,result); } break; case VM68K_INST_CLR: { retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,VM68K_LEGAL_DATAALTERATE,&ea); result=0; if (retval==VM68K_OK) retval=vm68k_calculateflags(&next,FLAGS_LOGIC,datatype,0,0,result); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype,ea,result); } break; case VM68K_INST_DBcc: { retval=VM68K_OK; displacement=READEXTENSIONWORD(pVM68k,&next); if (!vm68k_checkcondition(pVM68k,condition)) { next.d[reg2]&=0xffff0000; next.d[reg2]|=(pVM68k->d[reg2]-1)&0xffff; if ((tVM68k_sword)next.d[reg2]>=0) next.pcr=pVM68k->pcr+displacement; } } break; case VM68K_INST_SWAP: { tVM68k_types datatype2; datatype2=VM68K_LONG; retval=vm68k_resolve_ea(pVM68k,&next,datatype2,VM68K_AM_DATAREG,reg2,VM68K_LEGAL_AM_DATAREG,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype2,ea,&operand2); if (retval==VM68K_OK) result=((operand2>>16)&0xffff)|((operand2&0xffff)<<16); if (retval==VM68K_OK) next.nflag=(result>>31)&1; if (retval==VM68K_OK) next.zflag=(result==0); next.cflag=0; next.vflag=0; if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype2,ea,result); } break; case VM68K_INST_SCC: { tVM68k_types datatype2; datatype2=VM68K_BYTE; result=(vm68k_checkcondition(pVM68k,condition))?0xff:0x00; retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_DATAALTERATE,&ea); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype2,ea,result); } break; case VM68K_INST_TST: { retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,VM68K_LEGAL_DATAALTERATE,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype,ea,&operand2); if (retval==VM68K_OK) result=operand2; if (retval==VM68K_OK) retval=vm68k_calculateflags(&next,FLAGS_LOGIC,datatype,0,0,result); } break; case VM68K_INST_UNKNOWN: retval=VM68K_NOK_UNKNOWN_INSTRUCTION; break; default: { #ifdef DEBUG_PRINT char tmp[64]; vm68k_get_instructionname(instruction,tmp); printf("UNIMPLEMENTED opcode %04X = %s\n",opcode,tmp); #else printf("UNIMPLEMENTED opcode %04X\n",opcode); #endif retval=VM68K_NOK_UNKNOWN_INSTRUCTION; } break; } if (retval==VM68K_OK) { pVM68k->pcr=next.pcr; pVM68k->sr&=0xffe0; if (next.override_sr==1) { pVM68k->sr=next.sr; } else { pVM68k->sr|=(next.cflag)<<0; pVM68k->sr|=(next.vflag)<<1; pVM68k->sr|=(next.zflag)<<2; pVM68k->sr|=(next.nflag)<<3; pVM68k->sr|=(next.xflag)<<4; } for (i=0;i<8;i++) { pVM68k->a[i]=next.a[i]; pVM68k->d[i]=next.d[i]; } if (next.mem_we) { for (i=0;ipMem, next.mem_addr[i],next.mem_value[i]& 0xff);break; case 1: WRITE_INT16BE(pVM68k->pMem,next.mem_addr[i],next.mem_value[i]& 0xffff);break; case 2: WRITE_INT32BE(pVM68k->pMem,next.mem_addr[i],next.mem_value[i]&0xffffffff);break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION;break; } } } } return retval; } int vm68k_getNextOpcode(void* hVM68k,unsigned short* opcode) { tVM68k* pVM68k=(tVM68k*)hVM68k; if (hVM68k==NULL) return VM68K_NOK_INVALID_PTR; if (opcode==NULL) return VM68K_NOK_INVALID_PTR; if (pVM68k->magic!=MAGICVALUE) return VM68K_NOK_INVALID_PARAMETER; *opcode=READ_INT16BE(pVM68k->pMem,pVM68k->pcr); pVM68k->pcr+=2; #ifdef DEBUG_PRINT { int i; char tmp[64]; tVM68k_instruction inst; printf("\n\n\n"); printf("pcr:%06x ",pVM68k->pcr); printf("INST:%04X ",*opcode); printf("CVZN:%d%d%d%d ", (pVM68k->sr>>0)&1,(pVM68k->sr>>1)&1,(pVM68k->sr>>2)&1,(pVM68k->sr>>3)&1); printf("D:"); for (i=0;i<8;i++) { printf("%08X:",pVM68k->d[i]); } printf(" A:"); for (i=0;i<8;i++) { printf("%08X:",pVM68k->a[i]); } { unsigned long long sum; sum=0; for (i=0;imemsize;i++) sum+=READ_INT32BE(pVM68k->pMem,i); printf("MEMSUM:%llX ",sum); } inst=vm68k_decode(*opcode); vm68k_get_instructionname(inst,tmp); printf(" --> %s\n",tmp); if (pVM68k->pcr<0x1c238) printf("\x1b[0m\n"); fflush(stdout); } #endif return VM68K_OK; } dmagnetic-0.32/src/engine/include/0000755000175000017500000000000014076357617016472 5ustar dettusdettusdmagnetic-0.32/src/engine/include/linea.h0000644000175000017500000000611314076357617017734 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LINEA_H #define LINEA_H #define LINEA_OK 0 #define LINEA_OK_QUIT 28844 #define LINEA_OK_RESTART 28816 #define LINEA_NOK_UNKNOWN_INSTRUCTION 1 #define LINEA_NOK_INVALID_PTR -1 #define LINEA_NOK_INVALID_PARAM -2 #define LINEA_NOK_NOT_ENOUGH_MEMORY -3 #include "picture.h" // callback pointers. typedef int (*cbLineAOutputChar)(void* context,char c,unsigned char controlD2,unsigned char flag_headline); typedef int (*cbLineAOutputString)(void* context,char* string,unsigned char controlD2,unsigned char flag_headline); typedef int (*cbLineAInputString)(void* context,int* len,char* string); typedef int (*cbLineADrawPicture)(void* context,tPicture* picture,int mode); typedef int (*cbLineASaveGame)(void* context,char* filename,void* ptr,int len); typedef int (*cbLineALoadGame)(void* context,char* filename,void* ptr,int len); // configuration functions int lineA_getsize(int* size); int lineA_init(void* hLineA,void* pSharedMem,int *sharedmemsize,void* pMag,int magsize,void* pGfx,int gfxsize); int lineA_configrandom(void* hLineA,char random_mode,unsigned int random_seed); int lineA_setEGAMode(void* hLineA,int egamode); int lineA_getVersion(void* hLineA,int* version); int lineA_setCBoutputChar(void* hLineA,cbLineAOutputChar pCB,void *context); int lineA_setCBoutputString(void* hLineA,cbLineAOutputString pCB,void* context); int lineA_setCBinputString(void* hLineA,cbLineAInputString pCB,void* context); int lineA_setCBDrawPicture(void* hLineA,cbLineADrawPicture pCB,void* context); int lineA_setCBSaveGame(void* hLineA,cbLineASaveGame pCB,void* context); int lineA_setCBLoadGame(void* hLineA,cbLineALoadGame pCB,void* context); int lineA_showTitleScreen(void* hLineA); // api int lineA_substitute_aliases(void* hLineA,unsigned short* opcode); int lineA_singlestep(void* hLineA,void* hVM68k,unsigned short opcode); #endif dmagnetic-0.32/src/engine/include/vm68k.h0000644000175000017500000000361714076357617017625 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef VM68K_H #define VM68K_H // the return value is =0, when everything is okay. #define VM68K_OK 0 #define VM68K_NOK_UNKNOWN_INSTRUCTION 1 #define VM68K_NOK_INVALID_PTR -1 #define VM68K_NOK_INVALID_PARAMETER -2 // this function tells how much memory needs to be mallocated for the handle; int vm68k_getsize(int* size); // as the name suggests, pSharedMem is shared among the cores. it typically holds the game code. int vm68k_init(void* hVM68k,void* pSharedMem,int sharedmemsize,int version); int vm68k_singlestep(void *hVM68k,unsigned short opcode); int vm68k_getNextOpcode(void* hVM68k,unsigned short* opcode); #endif dmagnetic-0.32/src/engine/linea/0000755000175000017500000000000014076357622016133 5ustar dettusdettusdmagnetic-0.32/src/engine/linea/gfx2loader.c0000644000175000017500000001207314076357617020343 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "vm68k_datatypes.h" #include "vm68k_macros.h" #include "linea.h" #include typedef struct _tGFX2_properties { tVM68k_bool is_animation; tVM68k_uword width; tVM68k_uword height; tVM68k_uword celnum; tVM68k_uword animnum; tVM68k_uword commandnum; tVM68k_uword framecnt; tVM68k_ulong offset_background; tVM68k_ulong offset_cels; tVM68k_ulong offset_animations; tVM68k_ulong offset_commands; } tGFX2_properties; int gfx2loader_findoffset(tVM68k_ubyte* gfx2buf,tVM68k_ulong gfx2bufsize,char* filename, tVM68k_slong* offset,tVM68k_slong* length) { int i; int j; int idx; int directorysize; *offset=0; *length=0; directorysize=READ_INT16LE(gfx2buf,4); // the size of the directory is always at this position. for (i=0;i OK } } return -1; } int gfx2loader_loadproperties(tGFX2_properties* pGFX2,tVM68k_ubyte* gfx2buf,tVM68k_ulong gfx2bufsize,tVM68k_ubyte version,char* filename) { int i; tVM68k_slong offset; tVM68k_slong length; tVM68k_slong idx; pGFX2->is_animation=0; pGFX2->width=0; pGFX2->height=0; pGFX2->framecnt=0; // first: find out where the picture/animation is within the gfx2buf // and find out it is an animation or not. if (!gfx2loader_findoffset(gfx2buf,gfx2bufsize,filename,&offset,&length)) { tVM68k_slong datasize; tVM68k_uword animword; idx=offset; pGFX2->offset_background=idx; datasize=READ_INT32ME(gfx2buf,idx+38); pGFX2->width=READ_INT16LE(gfx2buf,idx+42); pGFX2->height=READ_INT16LE(gfx2buf,idx+44); animword=READ_INT16LE(gfx2buf,idx+datasize+48); if (animword!=0x5ed0) { pGFX2->is_animation=1; // second: the animation cels. idx=offset+50+datasize; pGFX2->offset_cels=idx; pGFX2->celnum=READ_INT16LE(gfx2buf,idx); for (i=0;icelnum;i++) { int height1,width1,datasize1; int height2,width2,datasize2; datasize1=READ_INT32ME(gfx2buf,idx+4); width1=READ_INT16LE(gfx2buf,idx+8); height1=READ_INT16LE(gfx2buf,idx+10); width2=READ_INT16LE(gfx2buf,idx+datasize1+14); height2=READ_INT16LE(gfx2buf,idx+datasize1+16); if (height1==height2 && width1==width2) // the cel contains a transparency mask { datasize2=READ_INT16LE(gfx2buf,idx+datasize1+18); idx+=12+datasize1+6+datasize2; } else { // the cel does not contain a transparency mask. just width, height, size and a bitmap idx+=12+datasize1; } } idx+=2; // UNKNOWN // third: the animations, or "actors" pGFX2->offset_animations=idx; pGFX2->animnum=READ_INT16LE(gfx2buf,idx); idx+=2; idx+=2; // UNKNOWN for (i=0;ianimnum;i++) { int animsteps; animsteps=READ_INT16LE(gfx2buf,idx); idx+=2; idx+=2; // UNKNOWN idx+=2*animsteps; // X idx+=2*animsteps; // Y idx+=2*animsteps; // celnum idx+=2*animsteps; // UNKNOWN(*) } idx-=2; // remove the last UNKNOWN(*) // fourth: the commands, the "script" pGFX2->offset_commands=idx; pGFX2->commandnum=READ_INT16LE(gfx2buf,idx);idx+=2; pGFX2->framecnt=0; for (i=0;icommandnum;i++) { int cmd; cmd=READ_INT8BE(gfx2buf,idx);idx++; switch(cmd) { case 0x01: idx+=3;break; case 0x02: pGFX2->framecnt+=READ_INT8BE(gfx2buf,idx);idx++;break; case 0x03: break; case 0x04: idx+=3;break; case 0x05: idx+=3;break; default: break; } } } } return 0; } dmagnetic-0.32/src/engine/linea/linea.c0000644000175000017500000012157414076357617017405 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ //#define DEBUG_PRINT #include #include #include #include "linea.h" #include "gfx1loader.h" #include "vm68k_datatypes.h" #include "vm68k_macros.h" #define MAGICVALUE 0x42696e61 // ="Lina" typedef struct _tProperties { tVM68k_ubyte unknown1[5]; tVM68k_ubyte flags1; tVM68k_ubyte flags2; tVM68k_ubyte unknown2; tVM68k_uword parentobject; tVM68k_ubyte unknown3[2]; tVM68k_uword endflags; } tProperties; typedef struct _tLineA { tVM68k_ulong magic; tVM68k_ubyte version; // pointers to important memory sections. tVM68k_ubyte* pMem; tVM68k_ulong memsize; tVM68k_ulong codesize; tVM68k_ubyte* pStrings1; tVM68k_ulong string1size; // the strings for some adventures were stored in two parts, since they have gotten too big tVM68k_ulong string2size; // tVM68k_ubyte* pDict; tVM68k_ulong dictsize; tVM68k_ulong decsize; tVM68k_ubyte* pStringHuffman; tVM68k_ubyte* pUndo; tVM68k_ulong undosize; tVM68k_slong undopc; // pointers to the callback functions cbLineAOutputChar pcbOutputChar; void* contextOutputChar; cbLineAOutputString pcbOutputString; void* contextOutputString; cbLineAInputString pcbInputString; void* contextInputString; cbLineADrawPicture pcbDrawPicture; void* contextDrawPicture; cbLineASaveGame pcbSaveGame; void* contextSaveGame; cbLineALoadGame pcbLoadGame; void* contextLoadGame; // persistent memory for some A0xx instructions. tVM68k_slong random_state; tVM68k_bool random_mode; tVM68k_uword properties_offset; tVM68k_uword linef_subroutine; // version >0 tVM68k_uword linef_tab; // version >1 tVM68k_uword linef_tabsize; // version >1 tVM68k_uword properties_tab; // version >2 tVM68k_uword properties_size; // version >2 tVM68k_slong interrupted_byteidx; tVM68k_ubyte interrupted_bitidx; // input buffer queue. char inputbuf[256]; int level; int used; #define MAXMAGBUF 350000 #define MAXGFXBUF 3500000 // buffer for everything. Personally, I do not like restricting it. // on the other hand: I do not like mallocs spread around the code either. tVM68k_ubyte magbuf[MAXMAGBUF]; tVM68k_ulong magsize; tVM68k_ubyte gfxbuf[MAXGFXBUF]; tVM68k_ulong gfxsize; // prefer ega images when set. tVM68k_bool egamode; tPicture picture; } tLineA; tVM68k_ulong lineA_getrandom(tLineA* pLineA) { if (pLineA->random_mode==0) { pLineA->random_state*=1103515245ull; pLineA->random_state+=12345ull; } else { pLineA->random_state=rand(); } return pLineA->random_state&0x7fffffff; } int lineA_parsegamefiles(tLineA* pLineA,void* pMag,int magsize,void* pGfx,int gfxsize) { int idx; // first: store the mag and gfx data if (pMag==NULL) return LINEA_NOK_INVALID_PTR; if (magsize>MAXMAGBUF) return LINEA_NOK_NOT_ENOUGH_MEMORY; memcpy(pLineA->magbuf,pMag,magsize);pLineA->magsize=magsize; // lets start with the header. // @0 4 bytes "MaSc" // @4 9 bytes TODO // @13 1 byte version // @14 4 bytes codesize // @18 4 bytes string1size // @22 4 bytes string2size // @26 4 bytes dictsize // @30 4 bytes decsize // @34 4 bytes undosize // @38 4 bytes undopc // ---------- // @42 codesize bytes code // @42+codesize string1size bytes... // @... string2size // @... dictsize // @... undo if (pLineA->magbuf[0]!='M' || pLineA->magbuf[1]!='a' || pLineA->magbuf[2]!='S' || pLineA->magbuf[3]!='c') return LINEA_NOK_INVALID_PARAM; // since the magic word seemed to be okay, start reading the header. pLineA->version=pLineA->magbuf[13]; pLineA->codesize=READ_INT32BE(pLineA->magbuf,14); pLineA->string1size=READ_INT32BE(pLineA->magbuf,18); pLineA->string2size=READ_INT32BE(pLineA->magbuf,22); pLineA->dictsize=READ_INT32BE(pLineA->magbuf,26); pLineA->decsize=READ_INT32BE(pLineA->magbuf,30); pLineA->undosize=READ_INT32BE(pLineA->magbuf,34); pLineA->undopc=READ_INT32BE(pLineA->magbuf,38); ///////////////////////////////////// // the code section has to be copied into the shared memory section. // because the same section is writable. first: drain the memory. memset(pLineA->pMem,0,pLineA->memsize); if (pLineA->memsizecodesize) return LINEA_NOK_NOT_ENOUGH_MEMORY; pLineA->memsize=pLineA->codesize; if (pLineA->memsize<0x10000) pLineA->memsize=0x10000; // miminum memory size idx=42; // the header held 42 bytes. memcpy(pLineA->pMem,&pLineA->magbuf[idx],pLineA->codesize); idx+=pLineA->codesize; // every other sections is just read-only. they can stay where they are. // for conveniance reasons, i am adding the pointers. pLineA->pStrings1=&pLineA->magbuf[idx];idx+=pLineA->string1size+pLineA->string2size; pLineA->pDict=&pLineA->magbuf[idx];idx+=pLineA->dictsize; pLineA->pUndo=&pLineA->magbuf[idx];idx+=pLineA->undosize; pLineA->pStringHuffman=&pLineA->pStrings1[pLineA->decsize]; pLineA->gfxsize=gfxsize; if (pGfx!=NULL) { memcpy(pLineA->gfxbuf,pGfx,gfxsize); } return LINEA_OK; } // the purpose of this function is to load the properties for a specific object. int lineA_loadproperties(tLineA* pLineA,tVM68k* pVM68k,tVM68k_uword objectnum,tVM68k_ulong* retaddr,tProperties* pProperties) { tVM68k_ulong addr; int i; if (pLineA->version>2 && (objectnum>pLineA->properties_size)) { addr=(pLineA->properties_size-objectnum)^0xffff; // TODO: WTF? addr*=2; addr+=pLineA->properties_tab; objectnum=READ_INT16BE(pVM68k->pMem,addr); } addr=pLineA->properties_offset+14*objectnum; for (i=0;i<5;i++) { pProperties->unknown1[i]=pVM68k->pMem[addr+i]; } pProperties->flags1=pVM68k->pMem[addr+5]; pProperties->flags2=pVM68k->pMem[addr+6]; pProperties->unknown2=pVM68k->pMem[addr+7]; pProperties->parentobject=READ_INT16BE(pVM68k->pMem,addr+8); for (i=0;i<2;i++) { pProperties->unknown3[i]=pVM68k->pMem[addr+i+10]; } pProperties->endflags=READ_INT16BE(pVM68k->pMem,addr+12); if (retaddr!=NULL) *retaddr=addr; return LINEA_OK; } int lineA_getsize(int* size) { if (size==NULL) return LINEA_NOK_INVALID_PTR; *size=sizeof(tLineA); return LINEA_OK; } int lineA_showTitleScreen(void* hLineA) { tLineA* pLineA=(tLineA*)hLineA; if (hLineA==NULL) return LINEA_NOK_INVALID_PTR; // the pc version has title screens. it would be a shame not to show them. if (pLineA->gfxsize) { if (pLineA->gfxbuf[0]=='M' && pLineA->gfxbuf[1]=='a' && pLineA->gfxbuf[2]=='P' && pLineA->gfxbuf[3]=='3') { gfxloader_unpackpic(pLineA->gfxbuf,pLineA->gfxsize,pLineA->version,30,NULL,&pLineA->picture,0); if (pLineA->pcbDrawPicture!=NULL) { pLineA->pcbDrawPicture(pLineA->contextDrawPicture,&pLineA->picture,1); } if (pLineA->pcbOutputChar!=NULL) { pLineA->pcbOutputString(pLineA->contextOutputString,"Press Enter",0,0); } // wait for an ENTER pLineA->level=0; pLineA->pcbInputString(pLineA->contextInputString,&pLineA->level,pLineA->inputbuf); pLineA->level=0; } } return LINEA_OK; } int lineA_substitute_aliases(void* hLineA,unsigned short* opcode) { tVM68k_uword inst; inst=*opcode; if ((inst&0xfe00)==0xA400) {inst&=0x01ff;inst|=0x6100;} // BSR if ((inst&0xfe00)==0xA200) {inst=0x4e75;} // RTS if ((inst&0xfe00)==0xA600) {inst&=0x01ff;inst|=0x4a00;} // TST if ((inst&0xfe00)==0xA800) {inst&=0x01ff;inst|=0x4800;} // MOVEM, register to memory (=0x4800) if ((inst&0xfe00)==0xAA00) {inst&=0x01ff;inst|=0x4C00;} // MOVEM, memory to register (=0x4C00) *opcode=inst; return LINEA_OK; } int lineA_init(void* hLineA,void* pSharedMem,int *sharedmemsize,void* pMag,int magsize,void* pGfx,int gfxsize) { tLineA* pLineA=(tLineA*)hLineA; int retval; if (hLineA==NULL) return LINEA_NOK_INVALID_PTR; memset(pLineA,0,sizeof(tLineA)); pLineA->magic=MAGICVALUE; pLineA->random_mode=0; pLineA->random_state=12345; pLineA->pMem=pSharedMem; pLineA->memsize=*sharedmemsize; pLineA->pcbOutputChar=NULL; pLineA->contextOutputChar=NULL; pLineA->pcbOutputString=NULL; pLineA->contextOutputString=NULL; pLineA->pcbInputString=NULL; pLineA->contextInputString=NULL; pLineA->pcbDrawPicture=NULL; pLineA->contextDrawPicture=NULL; pLineA->pcbSaveGame=NULL; pLineA->contextSaveGame=NULL; pLineA->pcbLoadGame=NULL; pLineA->contextLoadGame=NULL; retval=lineA_parsegamefiles(pLineA,pMag,magsize,pGfx,gfxsize); *sharedmemsize=pLineA->memsize; return retval; } int lineA_configrandom(void* hLineA,char random_mode,unsigned int random_seed) { tLineA* pLineA=(tLineA*)hLineA; if (hLineA==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; if (random_mode!=0 && random_mode!=1) return LINEA_NOK_INVALID_PARAM; if (random_seed<1 || random_seed>0x7fffffff) return LINEA_NOK_INVALID_PARAM; pLineA->random_mode=random_mode; pLineA->random_state=random_seed; srand(random_seed); return 0; } int lineA_setEGAMode(void* hLineA,int egamode) { tLineA* pLineA=(tLineA*)hLineA; if (hLineA==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; if (egamode!=0 && egamode!=1) return LINEA_NOK_INVALID_PARAM; pLineA->egamode=egamode; return 0; } int lineA_getVersion(void* hLineA,int* version) { tLineA* pLineA=(tLineA*)hLineA; if (pLineA==NULL) return LINEA_NOK_INVALID_PTR; if (version==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; *version=pLineA->version; return LINEA_OK; } int lineA_singlestep(void* hLineA,void* hVM68k,unsigned short opcode) { tLineA* pLineA=(tLineA*)hLineA; tVM68k* pVM68k=(tVM68k*)hVM68k; int retval=LINEA_NOK_UNKNOWN_INSTRUCTION; if (pLineA==NULL) return LINEA_NOK_INVALID_PTR; if (pVM68k==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; if (pLineA->version!=0 && (opcode&0xf000)==0xf000) // version 1 introduced programmable subroutines. // with version 2, it became programmable { // PUSHLONGTOSTACK(pVM68k,&next,next.pcr); if (pLineA->version==1) { // push long to stack pVM68k->a[7]-=4; WRITE_INT32BE(pVM68k->pMem,pVM68k->a[7],pVM68k->pcr); pVM68k->pcr=(pLineA->linef_subroutine)%pVM68k->memsize; } else { tVM68k_uword idx; tVM68k_sword base; idx=opcode&0x7ff; if (idx>=pLineA->linef_tabsize) { // so... at linef_tab is a list of jump-points. // the virtually, the pc jumps onto the address given by the opcode, and from there another XXXX samples on. if (!(opcode&0x0800)) // when it is a call to a subroutine { // push long to stack pVM68k->a[7]-=4; WRITE_INT32BE(pVM68k->pMem,pVM68k->a[7],pVM68k->pcr); pVM68k->pcr=(pLineA->linef_subroutine)%pVM68k->memsize; } idx=(opcode|0x0800); idx^=0xffff; base=READ_INT16BE(pVM68k->pMem,(pLineA->linef_tab+2*idx)); pVM68k->pcr=(pLineA->linef_tab+2*idx+base)%pVM68k->memsize; // jump, then jump again. } else { // push long to stack pVM68k->a[7]-=4; WRITE_INT32BE(pVM68k->pMem,pVM68k->a[7],pVM68k->pcr); pVM68k->pcr=(pLineA->linef_subroutine)%pVM68k->memsize; } } return LINEA_OK; } if ((opcode&0xff00)!=0xa000) return LINEA_NOK_INVALID_PARAM; retval=LINEA_OK; if ((opcode&0xff)<0xdd || (pLineA->version < 4 && (opcode&0xff) < 0xe4) || (pLineA->version < 2 && (opcode&0xff) < 0xed)) { lineA_getrandom(pLineA); // advance the random generator } switch (opcode) { case 0xa000: // getchar // the way i implemented the input is this: // i have a buffer, inputbuf[256]. holding the last input string. // when this instruction is called, one character is read and written into D1. // // when the buffer is empty, the callback function for inputs is called first. { if (pLineA->level==pLineA->used) { pLineA->level=0; pLineA->used=0; if (pLineA->pcbInputString!=NULL) { pLineA->pcbInputString(pLineA->contextInputString,&pLineA->level,pLineA->inputbuf); // callback to fill the input buffer. } else { fprintf(stderr,"INTERNAL ERROR: no input set!\n"); exit(0); } } if (pLineA->level>pLineA->used) // still characters in the buffer? { pVM68k->d[1]=pLineA->inputbuf[pLineA->used]; // yes. take one out pLineA->used++; // increase the read pointer for the next time. } } break; case 0xa0de: // version 3 (corruption) introduced this. the other implementation wrote a 1 into D1. { pVM68k->d[1]&=0xffffff00; pVM68k->d[1]|=0x01; } break; case 0xa0df: // version 3 (corruption) introduced this. { tVM68k_ubyte picname[9]; tVM68k_ubyte datatype; int i; datatype=READ_INT8BE(pVM68k->pMem,pVM68k->a[1]+2); for (i=0;i<8;i++) { picname[i]=READ_INT8BE(pVM68k->pMem,pVM68k->a[1]+3+i); } picname[8]=0; switch (datatype) { case 7: // show picture if (pLineA->gfxsize) { gfxloader_unpackpic(pLineA->gfxbuf,pLineA->gfxsize,pLineA->version,-1,picname,&pLineA->picture,pLineA->egamode); if (pLineA->pcbDrawPicture!=NULL) { pLineA->pcbDrawPicture(pLineA->contextDrawPicture,&pLineA->picture,2); } } break; default: break; } } break; case 0xa0e0: // unknown break; case 0xa0e1: // getstring, new feature by corruption! (version4) { int i; // the way i implemented the input is this: // i have a buffer, inputbuf[256]. holding the last input string. // when this instruction is being called, the argument is the output pointer in A1. // up to 256 bytes may be written there. A1 itself is being incremented, until // the end of the output is being reached. // // when the amount of bytes is either 256 or 1, D1 is set to 1. (TODO: why?) // // when the buffer is empty, the callback function for inputs is called first. lineA_getrandom(pLineA); // advance the random generator if (pLineA->level==pLineA->used) { pLineA->level=0; pLineA->used=0; if (pLineA->pcbInputString!=NULL) { pLineA->pcbInputString(pLineA->contextInputString,&pLineA->level,pLineA->inputbuf); // callback to fill the input buffer. } else { fprintf(stderr,"INTERNAL ERROR: no input set!\n"); exit(0); } } i=0; if (pLineA->level>pLineA->used) // still characters in the buffer? { tVM68k_ubyte c; do { c=pLineA->inputbuf[pLineA->used]; if (c==0) c='\n'; // apparently, the virtual machine wants its strings CR terminated. WRITE_INT8BE(pVM68k->pMem,(pVM68k->a[1]+i),c); pLineA->used++; // increase the read pointer for the next time. i++; } while (i<256 && pLineA->level>pLineA->used && c!='\n'); } pVM68k->a[1]+=(i-1); pVM68k->d[1]&=0xffff0000; if (i==256 || i==1) { pVM68k->d[1]|=1; } } break; case 0xa0e3: // this one apparently erases the picture if (pVM68k->d[1]==0) { if (pLineA->version<4 || pVM68k->d[6]==0) { // TODO: clear window } } break; case 0xa0e4: { pVM68k->a[7]+=4; // increase the stack pointer? maybe skip an entry or something? pVM68k->pcr=READ_INT32BE(pVM68k->pMem,pVM68k->a[7])%pVM68k->memsize; pVM68k->a[7]+=4; } break; case 0xa0e5: // set the Z-flag, RTS, introduced with jinxter. case 0xa0e6: // clear the Z-flag, RTS, introduced with jinxter. case 0xa0e7: // set the Z-flag, introduced with jinxter. case 0xa0e8: // clear the Z-flag, introduced with jinxter. { if (opcode==0xa0e5 || opcode==0xa0e7) // set zflag { pVM68k->sr|=(1<<2); // BIT 2 is the Z-flag } else { // clear z-flag pVM68k->sr&=~(1<<2); // BIT 2 is the Z-flag } if (opcode==0xa0e4 || opcode==0xa0e5 || opcode==0xa0e6) { // RTS: poplongfromstack(pcr); pVM68k->pcr=READ_INT32BE(pVM68k->pMem,pVM68k->a[7])%pVM68k->memsize; pVM68k->a[7]+=4; } } break; case 0xa0e9: { // strcpy a word from the dictionary into the memory. // source is in A1 // destination is A0 tVM68k_ubyte tmp; do { tmp=pLineA->pDict[pVM68k->a[1]++]; pVM68k->pMem[pVM68k->a[0]++]=tmp; } while (!(tmp&0x80)); } break; case 0xa0ea: // print a word from the dictionary. the beginning INDEX is stored in A1. the headline flag is signalled in D1. { unsigned char c; tVM68k_ubyte* dictptr; tVM68k_uword dictidx; if (pLineA->pDict==NULL || pLineA->dictsize==0) { retval=LINEA_NOK_INVALID_PTR; } else { dictptr=pLineA->pDict; dictidx=pVM68k->a[1]&0xffff; do { c=dictptr[dictidx++]; pLineA->pcbOutputChar(pLineA->contextOutputChar,c,pVM68k->d[2]&0xff,pVM68k->d[1]&0xff); } while (!(c&0x80)); pVM68k->a[1]&=0xffff0000; pVM68k->a[1]|=dictidx; } } break; case 0xa0eb: // write the byte stored in D1 into the dictionary at index A1 { pLineA->pDict[pVM68k->a[1]&0xffff]=pVM68k->d[1]&0xff; } break; case 0xa0ec: // read one byte stored @A1 from the dictionary. write it into register D0. (jinxter) { pVM68k->d[1]&=0xffffff00; pVM68k->d[1]|=pLineA->pDict[pVM68k->a[1]&0xffff]&0xff; } break; case 0xa0ed: // quit { retval=LINEA_OK_QUIT; } break; case 0xa0ee: // restart { retval=LINEA_OK_RESTART; } break; case 0xa0f0: { //printf("\x1b[1;37;44mLINEA: show picture %d mode %d\x1b[0m\n",pVM68k->d[0],pVM68k->d[1]); if (pVM68k->d[1] && pLineA->gfxsize) { gfxloader_unpackpic(pLineA->gfxbuf,pLineA->gfxsize,pLineA->version,pVM68k->d[0],NULL,&pLineA->picture,pLineA->egamode); if (pLineA->pcbDrawPicture!=NULL) { pLineA->pcbDrawPicture(pLineA->contextDrawPicture,&pLineA->picture,pVM68k->d[1]); } } } break; case 0xa0f1: { // skip some words in the input buffer tVM68k_ubyte* inputptr; tVM68k_uword inputidx; tVM68k_ubyte cinput; int i,n; inputptr=&pVM68k->pMem[pVM68k->a[1]&0xffff]; inputidx=0; n=(pVM68k->d[0])&0xffff; for (i=0;ia[1]+=inputidx; } break; case 0xa0f2: { tVM68k_uword objectnum; tProperties properties; int n; tVM68k_bool found; objectnum=(pVM68k->d[2])&0x7fff; n=pVM68k->d[4]&0x7fff; pVM68k->d[0]&=0xffff0000; pVM68k->d[0]|=pVM68k->d[2]&0xffff; found=0; retval=lineA_loadproperties(pLineA,pVM68k,objectnum,&pVM68k->a[0],&properties); do { if (properties.endflags&0x3fff) { found=1; } else { retval=lineA_loadproperties(pLineA,pVM68k,objectnum-1,NULL,&properties); if (objectnum==n) found=1; else objectnum--; } } while ((objectnum!=0) && !found); if (found) pVM68k->sr|=(1<<0); // bit 0 is the cflag pVM68k->d[2]&=0xffff0000; pVM68k->d[2]|=objectnum&0xffff; } break; case 0xa0f3: if (pLineA->pcbOutputChar!=NULL) { pLineA->pcbOutputChar(pLineA->contextOutputChar,pVM68k->d[1],pVM68k->d[2]&0xff,pVM68k->d[3]&0xff); } break; case 0xa0f4: { // TODO: version 0. The filename starts at A0. if (pLineA->pcbSaveGame!=NULL) { pLineA->pcbSaveGame(pLineA->contextSaveGame, (char*)&pVM68k->pMem[(pVM68k->a[0]&0xffff)], // filename &pVM68k->pMem[(pVM68k->a[1]&0xffff)], // ptr (pVM68k->d[1]&0xffff) // len ); } } break; case 0xa0f5: { // TODO: VERSION 0: filename starts at A0 if (pLineA->pcbLoadGame!=NULL) { pLineA->pcbLoadGame(pLineA->contextLoadGame, (char*)&pVM68k->pMem[(pVM68k->a[0]&0xffff)], // filename &pVM68k->pMem[(pVM68k->a[1]&0xffff)], // ptr (pVM68k->d[1]&0xffff) // len ); } } break; case 0xa0f6: // get random number (word), modulo D1. { tVM68k_ulong rand; tVM68k_uword limit; rand=lineA_getrandom(pLineA); // advance the random generator limit=(pVM68k->d[1])&0xff; if (limit==0) limit=1; rand%=limit; pVM68k->d[1]&=0xffff0000; pVM68k->d[1]|=(rand&0xffff); } break; case 0xa0f7: { // get a random value between 0 and 255, and write it to D0. tVM68k_ulong rand; rand=lineA_getrandom(pLineA); // advance the random generator pVM68k->d[0]&=0xffffff00; pVM68k->d[0]|=((rand+(rand>>8))&0xff); } break; case 0xa0f8: // write string { // strings are huffman-coded. // version 0: 'string2' holds the decoding tree in the first 256 bytes. // and the offset addresses for the bit streams in string1. // modes have bit 7 set. // // when the string is terminated with a \0 it ends. // when the string terminates with the sequence " @", it will be // extended. // // the extension will have the cflag set. // tVM68k_ulong idx; tVM68k_uword tmp; tVM68k_ubyte val; tVM68k_ubyte prevval; tVM68k_ulong byteidx; tVM68k_ubyte bitidx; char string[4096]; // TODO int stringidx; char c; if (!(pVM68k->sr&(1<<0))) // cflag is in bit 0. { byteidx=0; // start the byteidx and bitidx counter bitidx=0; idx=pVM68k->d[0]&0xffff; if (idx==0) byteidx=idx; // version 0: string 2 holds the table to decode the strings. // the decoder table is 256 bytes long. afterwards, a bunch of pointers // to bit indexes follow. else byteidx=READ_INT16BE(pLineA->pStringHuffman,(0x100+2*idx)); tmp=READ_INT16BE(pLineA->pStringHuffman,0x100); if (tmp && idx>=tmp) { byteidx+=pLineA->string1size; } } else { byteidx=pLineA->interrupted_byteidx; bitidx=pLineA->interrupted_bitidx; } val=0; stringidx=0; do { prevval=val; val=0; while (!(val&0x80)) // terminal symbols have bit 7 set. { tVM68k_ubyte bit; bit=pLineA->pStrings1[byteidx]; if (bit>>(bitidx)&1) { val=pLineA->pStringHuffman[0x80+val]; // =1 -> go to the right } else { val=pLineA->pStringHuffman[ val]; // =0 -> go to the left } bitidx++; if (bitidx==8) { bitidx=0; byteidx++; } } val&=0x7f; // remove bit 7. c=val; string[stringidx++]=c; } while (val!=0 && !(prevval==' ' && val=='@')); // end markers for the string are \0 and " @" if (prevval==' ' && val=='@') // extend the string next time this function is being called. { pVM68k->sr|=(1<<0); // set the cflag. cflag=bit 0. pLineA->interrupted_byteidx=byteidx; pLineA->interrupted_bitidx=bitidx; } else { pVM68k->sr&=~(1<<0); // clear the cflag. cflag=bit 0. } string[stringidx]=0; // string has been assembled. and is 0 terminated now print it. // perform the callback so that the string becomes visible. TODO: extended strings might be a problem!! if (pLineA->pcbOutputChar!=NULL) { pLineA->pcbOutputString(pLineA->contextOutputString,string,pVM68k->d[2]&0xff,pVM68k->d[3]&0xff); } } break; case 0xa0f9: //get inventory item(d0) { // there is a list of parent objects // // apparently, the structure of the properties is as followed: // byte 0..4: UNKNOWN // byte 5: Flags. // bit 0: is_described // byte 6: some flags // =bit 7: worn // =bit 6: bodypart // =bit 3: room // =bit 2: hidden // byte 8/9: parent object. the player is =0x0000 // byte 10..13: UNKNOWN // the data structure is a list. tVM68k_bool found; tVM68k_uword objectnum1; tVM68k_uword objectnum2; tProperties properties; found=0; // go backwards from the objectnumber for (objectnum1=pVM68k->d[0];objectnum1>0 && !found;objectnum1--) { objectnum2=objectnum1; do { // search for the parent retval=lineA_loadproperties(pLineA,pVM68k,objectnum2,&pVM68k->a[0],&properties); objectnum2=properties.parentobject; if ((properties.flags1&1) //is described || (properties.flags2&0xcc)) // worn, bodypart, room or hidden { objectnum2=0; // break the loop } else if (properties.parentobject==0) found=1; if (!(properties.flags2&1)) { objectnum2=0; // break the loop } } while (objectnum2); } // set the z-flag when the object was found. otherwise clear it. pVM68k->sr&=~(1<<2); // zflag is bit 2 if (found) pVM68k->sr|=(1<<2); pVM68k->d[0]&=0xffff0000; pVM68k->d[0]|=(objectnum1+1)&0xffff; // return value } break; case 0xa0fa: { // search the properties database for a match with the entry in D2. // starting adress is stored in A0. D3 is the variable counter. // d4 is the limit. for (;D3a[0]; pattern=pVM68k->d[2]; byte0word1=pVM68k->d[5]; pVM68k->sr&=~(1<<0); // cflag is bit 0; for (i=(pVM68k->d[3]&0xffff);i<(pVM68k->d[4]&0xffff) && !found;i++) { if (byte0word1) { value=READ_INT16BE(pVM68k->pMem,addr); value&=0x3fff; } else { value= READ_INT8BE(pVM68k->pMem,addr); value&=0xff; } addr+=14; if (value==pattern) { found=1; pVM68k->a[0]=addr; pVM68k->sr|=(1<<0); // cflag is bit 0. } } pVM68k->d[3]=i; } break; case 0xa0fb: { // skip D2 many words in the dictionary, that is pointed at by A1 tVM68k_ubyte* dictptr; tVM68k_uword dictidx; tVM68k_ubyte cdict; int i; int n; dictidx=0; if (pLineA->version==0 || pLineA->pDict==NULL || pLineA->dictsize==0) { dictptr=&pVM68k->pMem[pVM68k->a[1]&0xffff]; } else { //dictptr=pLineA->pDict; dictptr=&pLineA->pDict[pVM68k->a[1]&0xffff]; } n=(pVM68k->d[2]&0xffff); for (i=0;id[2]&=0xffff0000; // that was a counter pVM68k->a[1]+=dictidx; } break; case 0xa0fc: // skip D0 many words in the input buffer, as well as the dictionary. { tVM68k_ubyte* dictptr; tVM68k_ubyte* inputptr; tVM68k_uword dictidx; tVM68k_uword inputidx; int i,n; dictidx=0; inputidx=0; if (pLineA->version==0 || pLineA->pDict==NULL || pLineA->dictsize==0) { dictptr=&pVM68k->pMem[pVM68k->a[0]&0xffff]; // TODO: version 0. } else { dictptr=&pLineA->pDict[pVM68k->a[0]&0xffff]; } inputptr=&pVM68k->pMem[pVM68k->a[1]&0xffff]; n=(pVM68k->d[0])&0xffff; for (i=0;id[0]&=0xffff0000; // d0 was used as a counter pVM68k->a[0]+=dictidx; pVM68k->a[1]+=inputidx; } break; case 0xa0fd: pLineA->properties_offset=pVM68k->a[0]; if (pLineA->version!=0) { // version 1 introduced line F instructions pLineA->linef_subroutine=(pVM68k->a[3]&0xffff); if (pLineA->version>1) { // version 2 instruduced programmable instructions pLineA->linef_tab=(pVM68k->a[5])&0xffff; pLineA->linef_tabsize=(pVM68k->d[7]+1)&0xffff; } if (pLineA->version>2) { pLineA->properties_tab=(pVM68k->a[6])&0xffff; pLineA->properties_size=(pVM68k->d[6]); } } break; case 0xa0fe: { // register D0 conatins an object number. calculate the address in memory tVM68k_sword objectnum; tVM68k_ulong objectidx; if (pLineA->version>2 && (pVM68k->d[0]&0x3fff)>pLineA->properties_size) { pVM68k->d[0]&=0xffff7fff; // objectidx=((pLineA->properties_size-(pVM68k->d[0]&0x3fff))^0xffff); // TODO: I THINK THIS IS JUST A MODULO!!! objectidx=((pVM68k->d[0]&0x3fff)-pLineA->properties_size)-1; objectnum=READ_INT16BE(pVM68k->pMem,pLineA->properties_tab+objectidx*2); } else { if (pLineA->version>=2) { pVM68k->d[0]&=0xffff7fff; } else { pVM68k->d[0]&=0x00007fff; } objectnum=pVM68k->d[0]&0x7fff; } objectnum&=0x3fff; pVM68k->a[0]=pLineA->properties_offset+objectnum*14; } break; case 0xa0ff: { // so, here's what i know: (version 0) // the dictonary is stored at A3. // the word entered at A6 // there is a "bank" in register D6 // // the data structure is more or less plain. but the last char of each word has bit 7 set. // special characters 0x81=ENDOFDICT 0x82=BANKSEPARATOR are used. // // input: (A6) // output: (A2) // dict: (A3) // objects: (A1) { tVM68k_ubyte* dtabptr; tVM68k_ubyte* inputptr; tVM68k_ubyte* outputptr; tVM68k_ubyte* dictptr; tVM68k_ubyte* objectptr; tVM68k_ubyte* adjptr; tVM68k_uword inputidx; tVM68k_uword outputidx; tVM68k_uword outputidx2; tVM68k_uword dictidx; tVM68k_uword objectidx; tVM68k_uword adjidx; tVM68k_uword wordidx; tVM68k_ubyte bank; tVM68k_ubyte flag; tVM68k_bool matching; tVM68k_ubyte cinput1,cinput2; tVM68k_ubyte cdict; tVM68k_bool matchfound; tVM68k_ulong wordmatch; tVM68k_uword longestmatch; tVM68k_ubyte flag2; int i,j; longestmatch=0; flag2=0; inputptr =&pVM68k->pMem[pVM68k->a[6]]; if (pLineA->version==0 || pLineA->pDict==NULL || pLineA->dictsize==0) { dictptr=&pVM68k->pMem[pVM68k->a[3]&0xffff]; dtabptr=&pVM68k->pMem[pVM68k->a[5]&0xffff]; // version>0 } else { dictptr=&pLineA->pDict[pVM68k->a[3]&0xffff]; dtabptr=&pLineA->pDict[pVM68k->a[5]&0xffff]; // version>0 } outputptr =&pVM68k->pMem[pVM68k->a[2]]; objectptr =&pVM68k->pMem[pVM68k->a[1]]; adjptr =&pVM68k->pMem[pVM68k->a[0]]; inputidx=dictidx=objectidx=outputidx=adjidx=0; pVM68k->d[0]&=0xffff0000; // this regsiter was used during the adjective search. pVM68k->d[1]&=0xffff0000; // this regsiter was used during the adjective search. flag=0; bank=(pVM68k->d[6]&0xff); wordidx=0; cdict=0; matching=1; cinput1=cinput2=0; matchfound=0; pVM68k->d[0]&=0xffff0000; // the way the first loop works is this: // character by character, a word from the dictionary is compared to the input. // when a mismatch happens, the beginning of the next word is searched. -> matching=0; // while (cdict!=0x81) // 0x81 is the end marker of the dictionary { cdict=dictptr[dictidx++]; if (cdict==0x82) // bank separator { flag=0; inputidx=0; wordidx=0; bank++; matching=1; } else if (matching) { // actively comparing cinput1=inputptr[inputidx++]; // the current character cinput2=inputptr[inputidx]; // and the next onea if (pLineA->version!=0) { if (cdict==0x5f && (cinput2!=0 || cinput1==' ')) { flag=0x80; // the dictionary uses _ to signal objects that consist of longer words. "can of worms" thus becomes "can_of_worms". the matcher has to find it. cinput1='_'; // replace the space from the input with an _ to see if there is a match. } } if (cdict&0x80) // the end of an entry in the dictionary is marked by bit 7 being set. { matchfound=0; if ((cinput1&0x5f)==(cdict&0x5f)) // still a match. wonderful. { if (cinput2==0x27) // rabbit's (Wonderland) { tVM68k_ubyte cinput3; inputidx++; cinput3=inputptr[inputidx]; // store the letter after the ' into register D0. for example: rabbit's -> store the S pVM68k->d[0]&=0xffff0000; pVM68k->d[0]|=(cinput3)&0xff; pVM68k->d[0]|=0x200; } if (cdict!=0xa0 || pLineA->version<4) // corruption started using " " as word separator for multi-word objects { if (cinput2==0 || cinput2==0x20 || cinput2==0x27) matchfound=1; // and the input word ends as well. perfect match. } } else { if (pLineA->version==0 && inputidx>7) matchfound=1; // the first 7 characters matched. good enough. matching=0; } } else { // keep comparing. if (pLineA->version!=0) // version 1 introduced objects with multiple words. { if (cinput1==' ' && cdict==0x5f) // multiple word entry found { flag=1; cinput1=0x5f; // multiple word entries are marked by a _ instead of a space. this one makes sure that the next if() will work. } } if ((cinput1&0x5f)!=(cdict&0x5f) || (cdict&0x5f)==0x00 // FIXME ) { if (cinput2==' ' && pLineA->version==0 && inputidx>=7) matchfound=1; // the first 7 characters matched. good enough. matching=0; // there was a mismatch. } } } if (matchfound) { // the matches are stored in the following format: // bit 31..24 are a flag, which is =0 in version 0. // bit 23..16 is the bank. // bit 15..0 contain the matched word number in the bank. wordmatch =(((tVM68k_ulong)flag)<<24); wordmatch|=(((tVM68k_ulong)bank)<<16); wordmatch|=((tVM68k_ulong)wordidx); if (inputidx>=longestmatch) longestmatch=inputidx; WRITE_INT32BE(outputptr,outputidx,wordmatch); // store the candidates in the output location. outputidx+=4; // length of the result: 4 bytes. matchfound=0; } if (cdict&0x80 && cdict!=0x82 && !(pLineA->version>4 && cdict==0xa0)) // when the end of the word is reached. bit 7 is set. { wordidx++; matching=1; // start over inputidx=0; // start over. flag=0; } } WRITE_INT16BE(outputptr,outputidx,0xffff);// the end marker in the buffer is a 0xffff. // the output buffer holds outputidx/4 many results. if (pLineA->version!=0) // version 1 introduced synonyms. { // search the list of output words for (i=0;i>24)&0xff; bank=(wordmatch>>16)&0xff; wordidx=wordmatch&0xffff; if (bank==0x0b) { tVM68k_uword substword; substword=READ_INT16BE(dtabptr,wordidx*2); // TODO: version >1??? // the lower 5 bits are the bank. // the upper 11 bits in the substitute database are the actual word index. bank=substword&0x1f; wordidx=substword>>5; wordmatch=flag;wordmatch<<=8; wordmatch|=(bank&0xff);wordmatch<<=16; wordmatch|=wordidx&0xffff; WRITE_INT32BE(outputptr,i,wordmatch); } } } outputidx2=0; adjidx=0; for (i=0;i>24)&0xff; bank=(wordmatch>>16)&0xff; wordidx=wordmatch&0xffff; mismatch=0; obj=READ_INT16BE(objectptr,objectidx); if (obj && bank==6) { // first step: skip the adjectives that are not meant for this word. each adjective list is separated by a 0. for (j=0;jd[1]&=0xffff0000; if (mismatch==0) { flag2|=flag; wordmatch =flag2&0xff;wordmatch<<=8; wordmatch|=bank&0xff;wordmatch<<=16; wordmatch|=wordidx&0xffff; WRITE_INT32BE(outputptr,outputidx2,wordmatch); outputidx2+=4; } else { pVM68k->d[1]|=1; } } pVM68k->a[5]=pVM68k->a[6]; // flag2 being set denotes that there has been an object that is occupying multiple words. if (flag2 && outputidx) // that match is probably a better one, so move it to the front of the output word list. { for (i=0;ia[5]=pVM68k->a[6]+(longestmatch-3); } } pVM68k->a[2]+=outputidx2; // pVM68k->d[0]=0; pVM68k->a[6]=pVM68k->a[5]+1; } } break; default: printf("\n \x1b[0;37;44mUNIMPLEMENTED LINEA opcode %04X\x1b[0m\n",opcode); break; } return retval; } int lineA_setCBoutputChar(void* hLineA,cbLineAOutputChar pCB,void *context) { tLineA* pLineA=(tLineA*)hLineA; if (pLineA==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; pLineA->pcbOutputChar=pCB; pLineA->contextOutputChar=context; return LINEA_OK; } int lineA_setCBoutputString(void* hLineA,cbLineAOutputString pCB,void* context) { tLineA* pLineA=(tLineA*)hLineA; if (pLineA==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; pLineA->pcbOutputString=pCB; pLineA->contextOutputString=context; return LINEA_OK; } int lineA_setCBinputString(void* hLineA,cbLineAInputString pCB,void* context) { tLineA* pLineA=(tLineA*)hLineA; if (pLineA==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; pLineA->pcbInputString=pCB; pLineA->contextInputString=context; return LINEA_OK; } int lineA_setCBDrawPicture(void* hLineA,cbLineADrawPicture pCB,void* context) { tLineA* pLineA=(tLineA*)hLineA; if (pLineA==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; pLineA->pcbDrawPicture=pCB; pLineA->contextDrawPicture=context; return LINEA_OK; } int lineA_setCBLoadGame(void* hLineA,cbLineALoadGame pCB,void* context) { tLineA* pLineA=(tLineA*)hLineA; if (pLineA==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; pLineA->pcbLoadGame=pCB; pLineA->contextLoadGame=context; return LINEA_OK; } int lineA_setCBSaveGame(void* hLineA,cbLineASaveGame pCB,void* context) { tLineA* pLineA=(tLineA*)hLineA; if (pLineA==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; pLineA->pcbSaveGame=pCB; pLineA->contextSaveGame=context; return LINEA_OK; } dmagnetic-0.32/src/engine/linea/gfx1loader.c0000644000175000017500000012451314076357617020345 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "vm68k_datatypes.h" #include "vm68k_macros.h" #include "gfx1loader.h" #include "linea.h" // for the picture #include #include int gfxloader_gfx1(tVM68k_ubyte* gfxbuf,tVM68k_ulong gfxsize,tVM68k_ubyte version,int picnum,tPicture* pPicture) { int i; int retval; tVM68k_ulong picoffs; tVM68k_uword width; tVM68k_uword height; tVM68k_uword tablesize; tVM68k_ulong datasize; tVM68k_uword tableidx; tVM68k_ulong byteidx; tVM68k_ubyte bitidx; tVM68k_ubyte curbyte; tVM68k_ubyte curpixel; pPicture->pictureType=PICTURE_DEFAULT; retval=0; picnum&=0xffff; picoffs=READ_INT32BE(gfxbuf,8+4*picnum); // the .gfx file starts with the index pointers to the actual picture data. // once the offset has been calculated, the actual picture data is as followed: // bytes 0..1: UNKNOWN // bytes 0x02..0x03: X1 // bytes 0x04..0x05: X2 // X2-X2=width // bytes 0x06..0x07: height // bytes 0x08..0x1b: UNKNOWN // bytes 0x1c..0x3b: palette. // bytes 0x3c..0x3d: size of the huffman table // bytes 0x3e..0x41: size of the data bit stream // // bytes 0x42+0x42+tablesize: huffman decoding table // bytes 0x42+tablesize..0x42+tablesize+datasize: bitstream curpixel=0; width=READ_INT16BE(gfxbuf,picoffs+4)-READ_INT16BE(gfxbuf,picoffs+2); height=READ_INT16BE(gfxbuf,picoffs+6); // this particular graphics format has 3 bits per RGB channel for (i=0;i<16;i++) { unsigned short s; unsigned int red,green,blue; s=READ_INT16BE(gfxbuf,picoffs+0x1c+2*i); red =(s>>8)&0xf; green =(s>>4)&0xf; blue =(s>>0)&0xf; red*=PICTURE_MAX_RGB_VALUE;green*=PICTURE_MAX_RGB_VALUE;blue*=PICTURE_MAX_RGB_VALUE; red/=7;green/=7;blue/=7; pPicture->palette[i]=(red<<(2*PICTURE_BITS_PER_RGB_CHANNEL))|(green<<(1*PICTURE_BITS_PER_RGB_CHANNEL))|blue; } tablesize=READ_INT16BE(gfxbuf,picoffs+0x3c); // size of the huffman table datasize =READ_INT32BE(gfxbuf,picoffs+0x3e); // size of the bitstream // the huffman table contains links. if a bit in the stream is set, the upper 8 bits, otherwise the lower ones. // terminal symbols have bit 7 set. bitidx=8; // MSB first byteidx=picoffs+0x42+tablesize*2+2; tableidx=0; curbyte=READ_INT8BE(gfxbuf,byteidx); byteidx++; for (i=0;(i>bitidx)&1) // bit was set. read the higher 8 bits. { nxt>>=8; } tableidx=(nxt)&0xff; // bit was not set. read the lower 8 bits. if (bitidx==0) { curbyte=READ_INT8BE(gfxbuf,byteidx); byteidx++; bitidx=8; // MSB first } } tableidx&=0x7f; // remove bit 7. if (tableidx>=0x10) // it bits 6..4 were set, the previous pixels are being repeated { tableidx-=0x10; } else { // since there are only 16 possible pixels, this is it. curpixel=tableidx; tableidx=1; // will become 0 in the next revelation. } } pPicture->pixels[i]=curpixel; tableidx--; } pPicture->height=height; pPicture->width=width; // the finishing touch: each line has to be XORed with the previous one. for (i=width;ipixels[i]^=pPicture->pixels[i-width]; } return retval; } int gfxloader_gfx2(tVM68k_ubyte* gfxbuf,tVM68k_ulong gfxsize,tVM68k_ubyte version,tVM68k_ubyte* picname,tPicture* pPicture) { int directorysize; int offset; //int length; int retval; int i; int j; int found; pPicture->width=0; pPicture->height=0; pPicture->pictureType=PICTURE_DEFAULT; // only gfx3 offers halftone pictures // the gfx2 buffer starts with the magic value, and then a directory directorysize=READ_INT16BE(gfxbuf,4); retval=0; // step 1: find the correct filename found=0; offset=-1; for (i=0;i>8)&0xf; green =(s>>4)&0xf; blue =(s>>0)&0xf; red*=PICTURE_MAX_RGB_VALUE;green*=PICTURE_MAX_RGB_VALUE;blue*=PICTURE_MAX_RGB_VALUE; red/=7;green/=7;blue/=7; pPicture->palette[i]=(red<<(2*PICTURE_BITS_PER_RGB_CHANNEL))|(green<<(1*PICTURE_BITS_PER_RGB_CHANNEL))|blue; } datasize=READ_INT32ME(gfxbuf,offset+38); pPicture->width=READ_INT16LE(gfxbuf,offset+42); pPicture->height=READ_INT16LE(gfxbuf,offset+44); // animmagic=READ_INT16LE(gfx2buf,offset+48+data_size); lumpsize=datasize/pPicture->height/4; // datasize=size of the picture. height: number of lines. thus: datasize/height=number of bytes per line. there are 4 lumps of bits per line. pixidx=0; for (y=0;yheight;y++) { tVM68k_ubyte byte0,byte1,byte2,byte3; tVM68k_ubyte mask; idx0=y*4*lumpsize+offset+48; idx1=idx0+1*lumpsize; idx2=idx0+2*lumpsize; idx3=idx0+3*lumpsize; for (x=0;xwidth;x+=8) { tVM68k_ubyte p; byte0=gfxbuf[idx0++]; byte1=gfxbuf[idx1++]; byte2=gfxbuf[idx2++]; byte3=gfxbuf[idx3++]; mask=(1<<7); // MSB FIRST for (i=0;i<8;i++) { p =(byte0&mask)?0x01:0; p|=(byte1&mask)?0x02:0; p|=(byte2&mask)?0x04:0; p|=(byte3&mask)?0x08:0; mask>>=1; if ((x+i)width) { pPicture->pixels[pixidx++]=p; } } } } } return retval; } #define MAXPICWIDTH 512 int gfxloader_gfx3(tVM68k_ubyte* gfxbuf,tVM68k_ulong gfxsize,tVM68k_ubyte version,int picnum,tPicture* pPicture) { // 0.. 3: 4 bytes "MaP3" // 4.. 8: 4 bytes length of index // 8..11: 4 bytes length of disk1.pix // 11..15: 4 bytes length of disk2.pix // then the index // then the disk1.pix data // then the disk2.pix data int retval; int offs1; int offs2; int offset; int indexoffs,indexlen; int disk1offs,disk1len; int disk2offs; int i,n; int huffsize; int tableidx; int byteidx; int unhufcnt; int pixelcnt; int state; unsigned char mask; unsigned char byte; unsigned int unpackedsize; int max_stipple; unsigned char pl_lut[128]; // lookup table for left pixels unsigned char pr_lut[128]; // lookup table for right pixels unsigned char xorbuf[MAXPICWIDTH*2]; // ring buffer, to perform an XOR over two lines of stipples unsigned char rgbbuf[16]; // RGB values are 6 bits wide. 2 bits red, 2 bits green, 2 bits blue. unsigned char last_stipple; int state_cnt; int height,width; if (!gfxsize) return 0; // there is no picture data available. nothing to do. picnum&=0xffff; pPicture->pictureType=PICTURE_HALFTONE; // this format offers half tones. indexlen=READ_INT32BE(gfxbuf, 4); disk1len=READ_INT32BE(gfxbuf, 8); // disk2len=READ_INT32BE(gfxbuf,12); indexoffs=16; disk1offs=indexoffs+indexlen; disk2offs=disk1offs+disk1len; retval=0; // step 1: find the offset of the picture within the index. // the way it is stored is that the offsets within disk1 are stored in the first half, // and the offsets for disk2 are in the second half. // in case the offset is -1, it must be in the other one. offs1=(tVM68k_slong)READ_INT32LE(gfxbuf,indexoffs+picnum*4); offs2=(tVM68k_slong)READ_INT32LE(gfxbuf,indexoffs+indexlen/2+picnum*4); if (picnum!=30 && offs1!=-1 && offs2!=-1) offs2=-1; // in case one picture is stored on both disks, prefer the first one. if (picnum==30 && offs1==-1 && offs2==-1) offs1=0; // special case: the title screen for the GUILD of thieves is the first picture in DISK1.PIX if (offs1!=-1) offset=offs1+disk1offs; // in case the index was found in the first half, use disk1 else if (offs2!=-1) offset=offs2+disk2offs; // in case the index was found in the second half, use disk2 else return -1; /// otherwise: ERROR if (offset>gfxsize) // this is MYTH: there is only a single image file. { offset=offs1; } // the picture is stored in layers. // the first layer is a hufman table. // this unpacks the second layer, which contains repitions // and a "stipple" table. from this, the actual pixels are being // calculated. huffsize=gfxbuf[offset+0]; unpackedsize=READ_INT16BE(gfxbuf,offset+huffsize+1); unpackedsize*=4; unpackedsize+=3; unpackedsize&=0xffff; // it was designed for 16 bit machines. pixelcnt=-1; unhufcnt=0; state=0; tableidx=0; mask=0; byteidx=offset+huffsize+2+1; // the beginning of the bitstream starts after the hufman table and the unpackedsize byte=0; width=0; height=0; memset(xorbuf,0,sizeof(xorbuf)); // initialize the xor buffer with 0 state_cnt=0; max_stipple=last_stipple=0; while (unhufcnt>=1; // MSB first. if (b&0x80) // leaves have the highest bit set. terminal symbols only have 7 bit. { tableidx=0; b&=0x7f; // terminal symbols have 7 bit // // // the second layer begins here switch (state) { case 0: // first state: the ID should be "0x77" if (b!=0x77) return -1; // illegal format state=1; break; case 1: // second byte is the number of "stipples" max_stipple=b; state=2; break; case 2: // width, stored as 2*6 bit Big Endian width<<=6; // 2*6 bit. big endian; width|=b&0x3f; state_cnt++; if (state_cnt==2) state=3; break; case 3: // height, stored as 2*6 bit Big Endian height<<=6; // 2*6 bit. big endian; height|=b&0x3f; state_cnt++; if (state_cnt==4) { if (height<=0 || width<=0) return -2; // error in decoding the height and the width pixelcnt=0; state_cnt=0; state=4; } break; case 4: // rgb values rgbbuf[state_cnt++]=b; if (state_cnt==16) { state_cnt=0; state=5; } break; case 5: // lookup-table to retrieve the left pixel value from the stipple pl_lut[state_cnt++]=b; if (state_cnt==max_stipple) { state_cnt=0; state=6; } break; case 6: // lookup-table to retrieve the right pixel value from the stipple pr_lut[state_cnt++]=b; if (state_cnt==max_stipple) { last_stipple=0; state_cnt=0; state=7; } break; case 7: case 8: // now for the stipple table // this is actually a third layer of encoding. // it contains terminal symbols [0... max_stipple) // // if the symbol is max_stipple, it means that the previous symbol is being repeated. n=0; if (state==8) // this character has been "escaped" { state=7; n=1; last_stipple=b; } else if (b=max_stipple. { // this is necessary for the XOR operation. state=8; n=0; } else if (b>max_stipple) { n=b-max_stipple; // repeat the previous stipple b=last_stipple; } } for (i=0;ipixels[pixelcnt++]=pl_lut[x]; pPicture->pixels[pixelcnt++]=pr_lut[x]; } break; } } else { tableidx=b; // non terminal -> traverse the tree further down } } pPicture->height=height; pPicture->width=width*2; // the other image formats have 9 bit wide rgb values. for (i=0;i<16;i++) { unsigned char halftonelut[4]={0,2,5,7}; unsigned int red,green,blue; red =(rgbbuf[i]>>4)&0x3; green=(rgbbuf[i]>>2)&0x3; blue =(rgbbuf[i]>>0)&0x3; red =(halftonelut[red]*PICTURE_MAX_RGB_VALUE)/7; green =(halftonelut[green]*PICTURE_MAX_RGB_VALUE)/7; blue =(halftonelut[blue]*PICTURE_MAX_RGB_VALUE)/7; pPicture->palette[i]=(red<<(2*PICTURE_BITS_PER_RGB_CHANNEL))|(green<<(1*PICTURE_BITS_PER_RGB_CHANNEL))|blue; } return retval; } // the gfx4 format is used to handle the pictures from the Magnetic Windows system. // just like the gfx2, it starts with a directory. 6 byte picname, 4 bytes (little endian) offset, 4 bytes (little endian) length. // at the offset, the picture is being comprised of the tree (type 7) and image (type 6) from the Magnetic Windows resource files. // the tree is ALWAYS 609 bytes long. The size of the image varies. // // the huffman tree uses 9 bits to encode 8 bits: the first 32 bytes are a bitmask (MSB first). Then the branches/symbols follow. // if the bit from the bitmask is set, it is a terminal symbol. // 0x00...0x1f: LEFTMASK (0) // 0x20...0x11f: LEFTBRANCH // 0x120..0x13f: RIGHTMASK (1) // 0x140..0x23f: RIGHTBRANCH // Byte 0x240 escape character (for run level encoding) // Byte 0x241...0x250: EGA palette // Byte 0x251...0x260: EGA palette pairs // // // the image data looks like this: // 0x00..0x03: magic header // 0x04..0x23: 16x2 bytes RGB values (4 bits per channel, little endian, 0x0rgb) // 0x24..0x25: width // 0x26..0x27: height // 0x28..0x29: transparency placeholder // 0x2a..0x2b: size // 0x2c......: bit stream. MSB first. when the bit is set (=1), follow the RIGHT branch. // // the run level encoding is signalled by the escape character from the tree (byte 0x240). // in case the next character is 0xff, it actually an honest escape character. // if any other value follows, it is the repeat num. // the character AFTER that one is being repeated (4+repeat) number of times. // // each symbol is 8 bits large, 4 bits are the pixel. CAREFUL: bits 0..3 are the left, bits 4..7 are the right pixel // in case the width is not divisible by 2, bits 4..7 are being ignored. // // the xor is being performed line by line. // // note that the end of the image comes BEFORE the end of the bitstream. (due to a bug in the encoder) int gfxloader_gfx4(tVM68k_ubyte* gfxbuf,tVM68k_ulong gfxsize,tVM68k_ubyte version,tVM68k_ubyte* picname,tPicture* pPicture,int egamode) { #define SIZEOFTREE 609 int directorysize; int retval; int found; int i; int offset,length; int offset_vanilla,length_vanilla; int offset_ega,length_ega; int offset_anim,length_anim; int j; pPicture->width=0; pPicture->height=0; pPicture->pictureType=PICTURE_DEFAULT; // the gfx4 buffer starts with the magic value, and then a directory retval=0; found=0; directorysize=READ_INT16BE(gfxbuf,4); offset=offset_ega=offset_anim=offset_vanilla=-1; length=length_ega=length_anim=length_vanilla=-1; for (i=0;i=15); stillimage=1; if (READ_INT32LE(gfxbuf,offset+length-4)!=STILLMAGIC) stillimage=0; // if they do not, it is an animation found=0; if (!ega && stillimage) { offset_vanilla=offset; length_vanilla=length; } if (ega) { offset_ega=offset; length_ega=length; } if (!stillimage) { offset_anim=offset; length_anim=length; } } } // in case the image was not found offset=-1; length=-1; if (egamode) { offset=offset_ega; length=length_ega; } if (offset==-1) { offset=offset_vanilla; length=length_vanilla; } if (offset==-1) { offset=offset_anim; length=length_anim; } if (offset==-1) { offset=offset_ega; length=length_ega; } if (offset!=-1 && length!=-1) found=1; if (found) { int treestart; int picstart; int size; int treeidx; int repnum; int rlestate; // for the run length encoding. state 0: if not the escape char, output. otherwise -> state 1. in state 1, if the symbol is 0xff, the output is the escapechar. otherwise, the repition number (sans 4) -> state 4. in state 4, repeat the symbol tVM68k_ubyte escapechar; tVM68k_ubyte byte; tVM68k_ubyte mask; treestart=offset+0; picstart=offset+SIZEOFTREE; // byte 0x240 in the tree is the escape symbol for the run level encoding escapechar=gfxbuf[treestart+0x240]; // bytes 0x04..0x23: RGB values for (i=0;i<16;i++) { unsigned short s; unsigned int red,green,blue; s=READ_INT16LE(gfxbuf,picstart+0x4+i*2); red =(s>>8)&0xf; green =(s>>4)&0xf; blue =(s>>0)&0xf; red*=PICTURE_MAX_RGB_VALUE;green*=PICTURE_MAX_RGB_VALUE;blue*=PICTURE_MAX_RGB_VALUE; red/=7;green/=7;blue/=7; pPicture->palette[i]=(red<<(2*PICTURE_BITS_PER_RGB_CHANNEL))|(green<<(1*PICTURE_BITS_PER_RGB_CHANNEL))|blue; } // bytes 0x24,0x25= width // bytes 0x26,0x27= height pPicture->width=READ_INT16LE(gfxbuf,picstart+0x24); pPicture->height=READ_INT16LE(gfxbuf,picstart+0x26); // bytes 0x2a,0x2b= size of the bitstream (in bytes) size=READ_INT16LE(gfxbuf,picstart+0x2a); j=0; treeidx=0; mask=0;byte=0; i=0; rlestate=0; repnum=1; // i is counting up the bytes in the bitstream. // j is counting up the pixels of the image while (((i<(length-SIZEOFTREE) && iwidth*pPicture->height)) { tVM68k_ubyte lterm,rterm,term; tVM68k_ubyte lbranch,rbranch,branch; // the bitmask is denoting (MSB first) if an entry is a terminal symbol (=1) or a branch (=0) lterm=gfxbuf[treestart+0x00 +treeidx/8]&(0x80>>(treeidx%8)); rterm=gfxbuf[treestart+0x120+treeidx/8]&(0x80>>(treeidx%8)); // the entry in the table could either be a branch or a terminal symbol lbranch=gfxbuf[treestart+0x20 +treeidx]; rbranch=gfxbuf[treestart+0x140+treeidx]; if (mask==0) { mask=0x80; byte=gfxbuf[picstart+i+0x2c]; i++; } term =(byte&mask)? rterm:lterm; branch=(byte&mask)?rbranch:lbranch; mask>>=1; if (term) { if (rlestate==0) { if (branch==escapechar) { rlestate=1; } else { repnum=1; } } else if (rlestate==1) { if (branch==0xff) { branch=escapechar; repnum=1; rlestate=0; } else { repnum=branch+4; // this form of RLE makes sense when the same byte was repeated 4 or more times in the source picture rlestate=2; } } else if (rlestate==2) { rlestate=0; // the current entry is a terminal symbol, which is going to be repeated } if (rlestate==0) { while (repnum && j<(pPicture->width*pPicture->height)) { // the lower 4 bits are the LEFT pixel pPicture->pixels[j]=branch&0xf; j++; // one byte holds two pixels. but when the width is not divisible by 2, drop the remaining nibble. if (j%pPicture->width) // the higher 4 bits are the RIGHT pixel; but only if it is not outside the scope of the image. { pPicture->pixels[j]=(branch>>4)&0xf; // the higher 4 bytes are the RIGHT pixel j++; } repnum--; } } treeidx=0; // go back to the start; } else { // not a terminal symbol. keep following the branches treeidx=branch; } } // the finishing touch: XOR each line with the previous one for (i=pPicture->width;iheight*pPicture->width;i++) { pPicture->pixels[i]^=pPicture->pixels[i-pPicture->width]; } } return retval; } // the Commodore C64 pictures // The c64 pictures consist of two parts: the bitmap and the colour map. essentially, each 8x8 block can be rendered with 4 colours. // 2 of them are fixed, the others are determined by the colourmap. int gfxloader_gfx5(unsigned char* gfxbuf,int gfxsize,int version,int picnum,tPicture* pPicture) { #define C64_PICWIDTH 160 #define C64_PICHEIGHT 152 #define C64_PIXELPERBYTE (8/2) #define C64_BYTES_BITMAP (C64_PICWIDTH*C64_PICHEIGHT/C64_PIXELPERBYTE) #define C64_BYTES_BITMAP_PAWN ((C64_PICWIDTH*C64_PICHEIGHT/C64_PIXELPERBYTE)+64) // THE Pawn had a little bit padding #define C64_BYTES_COLOURMAP_V0 380 // #define C64_BYTES_COLOURMAP_V1 (2*C64_BYTES_COLOURMAP_V0) #define C64_BYTES_COLOURMAP_HIGH 760 #define C64_MAXBYTES_PICTURE_V0 (C64_BYTES_BITMAP+C64_BYTES_COLOURMAP_V0+C64_BYTES_COLOURMAP_HIGH) #define C64_MAXBYTES_PICTURE_V1 (C64_BYTES_BITMAP+C64_BYTES_COLOURMAP_V1+C64_BYTES_COLOURMAP_HIGH) #define C64_MAXBYTES_PICTURE C64_MAXBYTES_PICTURE_V1 unsigned char tmpbuf[C64_MAXBYTES_PICTURE]; // maximum size for a picture. plus room for the threebuf unsigned char colour[4]={0}; int format; int i; int offs; const unsigned int gfx5_rgbvalues[16]={ 0x00000000, 0x3fffffff, 0x205330e0, 0x1d5ceb22, 0x2393c25d, 0x159ac934, 0x0b82c26d, 0x3b6f19c5, 0x239500a4, 0x15538000, 0x3126c5c5, 0x1284a128, 0x1ed7b5ed, 0x2a5ffe7d, 0x1c16d7ae, 0x2cab2aca}; for (i=0;i<16;i++) { pPicture->palette[i]=gfx5_rgbvalues[i]; } offs=READ_INT32BE(gfxbuf,4+4*picnum); pPicture->width=C64_PICWIDTH*2; // make it twice as wide as it actually is. pPicture->height=C64_PICHEIGHT; pPicture->pictureType=PICTURE_C64; // only gfx3 offers halftone pictures format=0; ///////////// dehuff ///////////// { int outcnt; int expected; int treeidx; int bitcnt; int byteidx; int threecnt; int rlenum; int rlecnt; unsigned char rlebuf[256]; unsigned char threebuf[3]={0}; unsigned char* ptr; unsigned char byte=0; unsigned char rlechar; treeidx=0; expected=C64_BYTES_BITMAP; outcnt=-1; bitcnt=0; ptr=&gfxbuf[offs]; byteidx=1+(ptr[0]+1)*2; // the first byte is the size of the huffman tree. After the huffman tree, the bit stream starts. threecnt=0; rlechar=0; rlenum=0; rlecnt=0; while (outcnt>4)&0xf; colour[2]=(tmpbuf[C64_BYTES_BITMAP_PAWN+colidx]>>0)&0xf; } else if (format==0x00) { colour[1]=(tmpbuf[C64_BYTES_BITMAP+C64_BYTES_COLOURMAP_V0+colidx]>>4)&0xf; colour[2]=(tmpbuf[C64_BYTES_BITMAP+C64_BYTES_COLOURMAP_V0+colidx]>>0)&0xf; if ((colidx%2)==0) { colour[3]=(tmpbuf[C64_BYTES_BITMAP+colidx/2]>>4)&0xf; } else { colour[3]=(tmpbuf[C64_BYTES_BITMAP+colidx/2]>>0)&0xf; } } else { colour[1]=(tmpbuf[C64_BYTES_BITMAP+C64_BYTES_COLOURMAP_V1+colidx]>>4)&0xf; colour[2]=(tmpbuf[C64_BYTES_BITMAP+C64_BYTES_COLOURMAP_V1+colidx]>>0)&0xf; colour[3]=(tmpbuf[C64_BYTES_BITMAP+colidx]&0xf); } for (i=0;i<8;i++) { int y2; unsigned char mask; y2=y+i; mask=tmpbuf[maskidx+i]; for (j=0;j<4;j++) { int x2; unsigned char col; x2=x+j; col=colour[(mask>>6)&0x3]; pPicture->pixels[0+2*x2+y2*(pPicture->width)]=col; // render the picture T W I C E as wide as it is. pPicture->pixels[1+2*x2+y2*(pPicture->width)]=col; mask<<=2; } } x+=4; if (x==C64_PICWIDTH) { x=0; y+=8; } } } return 0; } // the Amstrad CPC pictures int gfxloader_gfx6(unsigned char* gfxbuf,int gfxsize,int version,int picnum,tPicture* pPicture) { const unsigned char gfx6_codebook[16]={0x00,0x40,0x04,0x44,0x10,0x50,0x14,0x54,0x01,0x41,0x05,0x45,0x11,0x51,0x15,0x55}; const unsigned int gfx6_rgbvalues[27]={ 0x00000000,0x00000201,0x000003ff, 0x20100000,0x20100201,0x201003ff, 0x3ff00000,0x3ff00201,0x3ff003ff, 0x00080400,0x00080601,0x000807ff, 0x20180400,0x20180601,0x201807ff, 0x3ff80400,0x3ff80601,0x3ff807ff, 0x000ffc00,0x000ffe01,0x000fffff, 0x201ffc00,0x201ffe01,0x201fffff, 0x3ffffc00,0x3ffffe01,0x3fffffff }; int i; int paletteidx; int outidx; unsigned char byte; unsigned char mask; unsigned char symbol; unsigned char code; int bitidx; int toggle; int picoffs; int treeidx; int retval; retval=0; // find the index within the gfx buffer picoffs=READ_INT32BE(gfxbuf,4+picnum*4); treeidx=0; pPicture->height=152; // the size is always the same pPicture->width =160; paletteidx=0; outidx=0; byte=0; bitidx=picoffs+1+(gfxbuf[picoffs]+1)*2; mask=0; pPicture->palette[paletteidx++]=gfx6_rgbvalues[ 0]; // black pPicture->palette[paletteidx++]=gfx6_rgbvalues[26]; // bright white symbol=0; code=0; toggle=0; while ((outidx<(pPicture->height*pPicture->width))&& (bitidx>=1; if (branch&0x80) { treeidx=0; branch&=0x7f; if (paletteidx<16) // the first two colours are fixed. and the rest comes from the first 14 terminal symbols { pPicture->palette[paletteidx++]=gfx6_rgbvalues[branch]; // one of them } else { int loopcnt; loopcnt=1; if (branch&0x70) // if bits 6..4 are set, it is a loop. it determines how often the previous code is being repeated { loopcnt=branch-0x10; } else { // otherwise, it is a code code=gfx6_codebook[branch]; } for (i=0;iheight*pPicture->width);i++) { // the symbol is being combined from two codes symbol<<=1; symbol|=code; toggle=1-toggle; // when the symbol is finished if (toggle==0) { unsigned char p0; unsigned char p1; // the images are Amstrad Mode 0 pictures. Which means that the pixel bits are being interleaved. p0 =((symbol>>7)&0x1)<<0; p0|=((symbol>>3)&0x1)<<1; p0|=((symbol>>5)&0x1)<<2; p0|=((symbol>>1)&0x1)<<3; p1 =((symbol>>6)&0x1)<<0; p1|=((symbol>>2)&0x1)<<1; p1|=((symbol>>4)&0x1)<<2; p1|=((symbol>>0)&0x1)<<3; // at this point, the two pixels have been separated pPicture->pixels[outidx++]=p0; pPicture->pixels[outidx++]=p1; // prepare the next symbol symbol=0; } } } } else { treeidx=branch; } } // descramble the picture over two lines for (i=2*pPicture->width;iwidth*pPicture->height;i++) { pPicture->pixels[i]^=pPicture->pixels[i-2*pPicture->width]; } // make the picture wider for (i=pPicture->width*pPicture->height;i>=0;i--) { pPicture->pixels[2*i+0]=pPicture->pixels[i]; pPicture->pixels[2*i+1]=pPicture->pixels[i]; } pPicture->width*=2; return retval; } // Atari XL int gfxloader_gfx7(unsigned char* gfxbuf,int gfxsize,int version,int picnum,tPicture* pPicture) { int retval; int idx; unsigned char mask; unsigned char byte; unsigned char treesize; int treeidx; int treeoffs; int state; int rgbcnt; int rlenum; int rlecnt; unsigned char lc; unsigned char rlebuf[256]; unsigned char threebuf[3]; int threecnt; int pixcnt; int rlerep; int i; int blackcnt; retval=0; idx=READ_INT32BE(gfxbuf,4+4*picnum); treesize=gfxbuf[idx]; byte=0; mask=0; treeidx=0; treeoffs=idx+1; // idx=idx+treesize*2+3; idx=idx+treesize*2+3; threecnt=0; rlenum=0; rlecnt=0; rgbcnt=0; pixcnt=0; state=0; pPicture->width=160*2; // the original images were stored as 160x152 pixels. However, they look better when being scaled up to 320x152 pPicture->height=152; blackcnt=0; lc=0; while (idxwidth*pPicture->height && state!=3) { unsigned char branchl,branchr,branch; if (mask==0) { byte=gfxbuf[idx++]; mask=0x80; } branchl=gfxbuf[treeoffs+treeidx*2+0]; branchr=gfxbuf[treeoffs+treeidx*2+1]; branch=(byte&mask)?branchl:branchr; mask>>=1; if (branch&0x80) { treeidx=branch&0x7f; } else { treeidx=0; if (threecnt!=3) { threebuf[threecnt++]=branch; } else { int j; for (i=0;i>4)&0xf; brightness=(c>>0)&0xf; red_dark =(gfx7_ataripalette[basecolor][0]>>(2*PICTURE_BITS_PER_RGB_CHANNEL))&0x3ff; green_dark =(gfx7_ataripalette[basecolor][0]>>(1*PICTURE_BITS_PER_RGB_CHANNEL))&0x3ff; blue_dark =(gfx7_ataripalette[basecolor][0]>>(0*PICTURE_BITS_PER_RGB_CHANNEL))&0x3ff; red_bright =(gfx7_ataripalette[basecolor][1]>>(2*PICTURE_BITS_PER_RGB_CHANNEL))&0x3ff; green_bright =(gfx7_ataripalette[basecolor][1]>>(1*PICTURE_BITS_PER_RGB_CHANNEL))&0x3ff; blue_bright =(gfx7_ataripalette[basecolor][1]>>(0*PICTURE_BITS_PER_RGB_CHANNEL))&0x3ff; r=red_dark +((red_bright -red_dark)*brightness)/NUM_BRIGHTNESSLEVELS; g=green_dark +((green_bright -green_dark)*brightness)/NUM_BRIGHTNESSLEVELS; b=blue_dark +((blue_bright -blue_dark)*brightness)/NUM_BRIGHTNESSLEVELS; rgb=(r<<(2*PICTURE_BITS_PER_RGB_CHANNEL))|(g<<(1*PICTURE_BITS_PER_RGB_CHANNEL))|b; } pPicture->palette[rgbcnt++]=rgb; if (c==0) blackcnt++; if (rgbcnt==NUM_BASECOLOURS) { state=1; if (treesize==0x3e) state=1; else state=2; if (blackcnt<12) state=3; } break; case 1: // rle lookup table if (rlenum==0) { rlenum=c; rlecnt=0; if (rlenum==128 || rlenum==1) rlenum=0; } else { rlebuf[rlecnt++]=c; } if (rlenum==rlecnt) state=2; break; case 2: // and the pixel information rlerep=0; for (j=0;jpixels[pixcnt++]=(lc>>6)&0x3; pPicture->pixels[pixcnt++]=(lc>>6)&0x3; pPicture->pixels[pixcnt++]=(lc>>4)&0x3; pPicture->pixels[pixcnt++]=(lc>>4)&0x3; pPicture->pixels[pixcnt++]=(lc>>2)&0x3; pPicture->pixels[pixcnt++]=(lc>>2)&0x3; pPicture->pixels[pixcnt++]=(lc>>0)&0x3; pPicture->pixels[pixcnt++]=(lc>>0)&0x3; } break; } } threecnt=0; } } } if (rlenum!=0) { for (i=pPicture->width*2;ipixels[i]^=pPicture->pixels[i-pPicture->width*2]; } } return retval; } // Apple II picture loader int gfxloader_gfx8(unsigned char* gfxbuf,int gfxsize,int version,int picnum,tPicture* pPicture) { #define PICTURE_HOTFIX1 0x80000000 #define PICTURE_HOTFIX2 0x40000000 #define PICTURE_HOTFIX3 0x20000000 #define APPLE2_COLOURS 16 const unsigned int gfx8_apple2_palette[APPLE2_COLOURS]={// 10 bit per channel 0x00000000, // black 0x1814e2f6, // dark blue 0x000a3581, // dark green 0x050cfbf6, // medium blue 0x1817240c, // brown 0x2719c671, // dark grey 0x050f58f0, // light green 0x1c9fff42, // aquamarin 0x38e1e181, // deep red 0x3ff443f6, // purple 0x2719c671, // light grey 0x342c3bff, // light blue 0x3ff6a4f0, // orange 0x3ffa0742, // pink 0x342dda35, // yellow 0x3fffffff // white }; int retval; int i; unsigned int treeoffs; int hotfix; int outidx; int treeidx; unsigned char lastterm; unsigned char mask; unsigned char byte; unsigned char* unhuffptr; int pixidx; int offs4,offs3,offs2,offs1; int bitidx; retval=0; treeoffs=READ_INT32BE(gfxbuf,4*picnum+4); for (i=0;ipalette[i]=gfx8_apple2_palette[i]; } pPicture->height=192-32; pPicture->width=140*2; hotfix=(treeoffs&0xe0000000); if (hotfix==PICTURE_HOTFIX1) hotfix=-1; if (hotfix==PICTURE_HOTFIX2) hotfix= 1; if (hotfix==PICTURE_HOTFIX3) hotfix= 2; treeoffs&=0x1ffffff; treeoffs+=1; // step 1: unhuffing with the RLE outidx=0; treeidx=0; bitidx=treeoffs+gfxbuf[treeoffs-1]+2+hotfix; lastterm=1; mask=0; byte=0; unhuffptr=(unsigned char*)&(pPicture->pixels[pPicture->height*pPicture->width]); // at the unhuffptr, there is now the content which would have // been written into the Apple II Videoram at $2000. // the first 8192 bytes are the AUX memory bank. // the second 8192 bytes are the MAIN memory bank while (outidx<(8192+8192) && bitidx<=gfxsize) { unsigned char branchl,branchr; unsigned char branch; if (mask==0) { mask=0x80; byte=gfxbuf[bitidx++]; } branchl=gfxbuf[treeoffs+0+2*treeidx]; branchr=gfxbuf[treeoffs+1+2*treeidx]; branch=(byte&mask)?branchl:branchr;mask>>=1; if (branch&0x80) { unsigned char terminal; int n; terminal=branch&0x7f; if (lastterm==0 && outidx>3) { n=terminal-1; terminal=0; lastterm=1; } else { n=1; lastterm=terminal; } for (i=0;i>0)&0xf))<< 0); // pixel 0: A0 A1 A2 A3 bitreg|=((((unhuffptr[p+0x0000]>>4)&0x7)|((unhuffptr[p+0x2000]&0x1)<<3))<< 4); // pixel 1: A4 A5 A6 M0 bitreg|=((((unhuffptr[p+0x2000]>>1)&0xf))<< 8); // pixel 2: M1 M2 M3 M4 bitreg|=((((unhuffptr[p+0x2000]>>5)&0x3)|((unhuffptr[p+0x0001]&0x3)<<2))<<12); // pixel 3: M5 M6 B0 B1 bitreg|=((((unhuffptr[p+0x0001]>>2)&0xf))<<16); // pixel 4: B2 B3 B4 B5 bitreg|=((((unhuffptr[p+0x0001]>>6)&0x1)|((unhuffptr[p+0x2001]&0x7)<<1))<<20); // pixel 5: B6 N0 N1 N2 bitreg|=((((unhuffptr[p+0x2001]>>3)&0xf))<<24); // pixel 6: N3 N4 N5 N6 // bits have been collected, now turn them into pixels. for (j=0;j<7;j++) { unsigned char col; col=bitreg&0xf;bitreg>>=4; pPicture->pixels[pixidx++]=col; pPicture->pixels[pixidx++]=col; // make the pictures T W I C E as wide } } } } } } return retval; } int gfxloader_unpackpic(tVM68k_ubyte* gfxbuf,tVM68k_ulong gfxsize,tVM68k_ubyte version,int picnum,tVM68k_ubyte* picname,tPicture* pPicture,int egamode) { int retval; retval=0; picnum&=0x3f; // there are no more than 30 pictures in each game. except Wonderland. if (gfxbuf==NULL || pPicture==NULL) return -1; if (gfxbuf[0]=='M' && gfxbuf[1]=='a' && gfxbuf[2]=='P') { switch (gfxbuf[3]) { case 'i': retval=gfxloader_gfx1(gfxbuf,gfxsize,version,picnum,pPicture);break; // standard .mag/gfx format case '2': retval=gfxloader_gfx2(gfxbuf,gfxsize,version,picname,pPicture);break; // taken from the magnetic windows .gfx files case '3': retval=gfxloader_gfx3(gfxbuf,gfxsize,version,picnum,pPicture);break; // ms dos case '4': retval=gfxloader_gfx4(gfxbuf,gfxsize,version,picname,pPicture,egamode);break; // read from magnetic windows resource files case '5': retval=gfxloader_gfx5(gfxbuf,gfxsize,version,picnum,pPicture);break; // C64 case '6': retval=gfxloader_gfx6(gfxbuf,gfxsize,version,picnum,pPicture);break; // Amstrad CPC case '7': retval=gfxloader_gfx7(gfxbuf,gfxsize,version,picnum,pPicture);break; // AtariXL case '8': retval=gfxloader_gfx8(gfxbuf,gfxsize,version,picnum,pPicture);break; // Apple II default: break; } } return retval; } dmagnetic-0.32/src/engine/linea/gfx1loader.h0000644000175000017500000000323714076357617020351 0ustar dettusdettus/* Copyright 2021, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GFXLOADER_H #define GFXLOADER_H #include "vm68k_datatypes.h" #include "picture.h" // for the pPicture data type // this function is extracting the picture from the .gfx file and converts it into something easier to handle. int gfxloader_unpackpic(tVM68k_ubyte* gfxbuf,tVM68k_ulong gfxsize,tVM68k_ubyte version,int picnum,tVM68k_ubyte* picname,tPicture* pPicture,int egamode); #endif dmagnetic-0.32/build.sh0000644000175000017500000000247614076357617014457 0ustar dettusdettus#!/bin/sh #Copyright 2021, dettus@dettus.net # #Redistribution and use in source and binary forms, with or without modification, #are permitted provided that the following conditions are met: # #1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # #2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # #THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED #WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE #DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE #FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL #DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR #SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER #CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, #OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. make clean all