From 21b2306e9b3e8c0fad27031d6a192703a1f074d1 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Sun, 14 Oct 2018 20:50:14 -0500 Subject: [PATCH] Setting up new repository with just the JoeyLib code in it. --- .gitattributes | 2 + .gitignore | 7 + build-IIgs.sh | 50 ++++ build-PC.sh | 69 +++++ joeylib.pro | 45 +++ src/font.sta | Bin 0 -> 32036 bytes src/jIIgs.asm | 721 +++++++++++++++++++++++++++++++++++++++++++++ src/jIIgs.c | 332 +++++++++++++++++++++ src/jIIgs.macro | 119 ++++++++ src/jPC.c | 546 ++++++++++++++++++++++++++++++++++ src/joey.c | 763 ++++++++++++++++++++++++++++++++++++++++++++++++ src/joey.h | 261 +++++++++++++++++ src/kanga.sta | Bin 0 -> 32036 bytes src/music.mod | Bin 0 -> 43826 bytes src/stddclmr.h | 95 ++++++ src/test.c | 87 ++++++ 16 files changed, 3097 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100755 build-IIgs.sh create mode 100755 build-PC.sh create mode 100644 joeylib.pro create mode 100644 src/font.sta create mode 100644 src/jIIgs.asm create mode 100644 src/jIIgs.c create mode 100644 src/jIIgs.macro create mode 100644 src/jPC.c create mode 100644 src/joey.c create mode 100644 src/joey.h create mode 100644 src/kanga.sta create mode 100644 src/music.mod create mode 100644 src/stddclmr.h create mode 100644 src/test.c diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..4f5bb27 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.sta filter=lfs diff=lfs merge=lfs -text +*.mod filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d874ce5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*~ +*.user +lib/ +src/SDL2/ +src/music +src/music.w +src/*.dis diff --git a/build-IIgs.sh b/build-IIgs.sh new file mode 100755 index 0000000..f44ed30 --- /dev/null +++ b/build-IIgs.sh @@ -0,0 +1,50 @@ +#!/bin/bash -e + +if [ -d ${JOEY}/sdks/iix/IIgs/out/joey ]; then + rm -rf ${JOEY}/sdks/iix/IIgs/out/joey +fi +mkdir -p ${JOEY}/sdks/iix/IIgs/out/joey + +pushd ${JOEY}/joeylib/src +iix assemble jIIgs.asm keep=31:/out/joey/jIIgsasm +iix compile jIIgs.c keep=31:/out/joey/jIIgsc +iix compile joey.c keep=31:/out/joey/joey + +iix makelib 31:/out/joey/joeylib +31:/out/joey/jIIgsasm.A +iix makelib 31:/out/joey/joeylib +31:/out/joey/jIIgsasm.ROOT +iix makelib 31:/out/joey/joeylib +31:/out/joey/jIIgsc.a +iix makelib 31:/out/joey/joeylib +31:/out/joey/joey.a + +iix compile test.c keep=31:/out/joey/test +iix -DKeepType=S16 link 31:/out/joey/test 31:/out/joey/joeylib keep=31:/out/joey/test + +iix dumpobj +D 31:/out/joey/test &> test.dis || true + +php ${JOEY}/sdks/iix/ntconverter.php *.mod +popd + +rm JLSTATS || true +rm /tmp/import.po || true + +AC=${JOEY}/sdks/iix/ac/ +CADIUS=${JOEY}/sdks/iix/cadius/cadius.sh +IMPORT=/tmp/import.po + +${AC}/import.sh ${JOEY}/sdks/iix/IIgs/out/joey/test S16 +${AC}/import.sh ${JOEY}/joeylib/src/kanga.sta +${AC}/import.sh ${JOEY}/joeylib/src/font.sta +${AC}/import.sh ${JOEY}/joeylib/src/music MUS +${AC}/import.sh ${JOEY}/joeylib/src/music.w BIN + +cp -f ${JOEY}/joeylib/lib/IIgs/Tool221#ba0000 ${JOEY}/dist/IIgs/. +cp -f ${JOEY}/joeylib/src/joey.h ${JOEY}/dist/. +cp -f ${JOEY}/sdks/iix/IIgs/out/joey/joeylib ${JOEY}/dist/IIgs/joeylib#b20000 + +if [ ! -z $1 ]; then + pushd ${JOEY}/sdks/iix/gsplus + ./gsplus -config IIgsTest.cfg || true + popd + echo "" + ${AC}/export.sh JLSTATS + cat JLSTATS +fi diff --git a/build-PC.sh b/build-PC.sh new file mode 100755 index 0000000..ed60de9 --- /dev/null +++ b/build-PC.sh @@ -0,0 +1,69 @@ +#!/bin/bash -e + + +BUILD="${JOEY}/joeylib/build" +SRC="${JOEY}/joeylib/src" + + +function doBuild() { + if [ -d "${BUILD}" ]; then + rm -rf "${BUILD}" + fi + mkdir -p "${BUILD}" + if [ -d "${DIST}" ]; then + rm -rf "${DIST}" + fi + mkdir -p "${DIST}" + + pushd "${BUILD}" + ${CC} ${CFLAGS} -c -o jPC.o ${SRC}/jPC.c + ${CC} ${CFLAGS} -c -o joey.o ${SRC}/joey.c + cp -f *.o "${DIST}"/. + popd + + pushd ${DIST} + ar -x ${INSTALLED}/libSDL2.a + ar -x ${INSTALLED}/libSDL2_mixer.a + ar -x ${INSTALLED}/libmikmod.a + ar rcs joeylib.a *.o + rm *.o + popd +} + +CC="gcc" +CFLAGS="-Wall -D_REENTRANT -I${SRC}" +DIST="${JOEY}/dist/linux/x64" +INSTALLED="${JOEY}/SDL2/installed/linux/x64/lib" +doBuild + +CC="gcc" +CFLAGS="-m32 -Wall -D_REENTRANT -I${SRC}" +DIST="${JOEY}/dist/linux/x86" +INSTALLED="${JOEY}/SDL2/installed/linux/x86/lib" +doBuild + +CC="x86_64-w64-mingw32-gcc" +CFLAGS="-Wall -D_REENTRANT -I${SRC}" +DIST="${JOEY}/dist/windows/x64" +INSTALLED="${JOEY}/SDL2/installed/windows/x64/lib" +doBuild + +CC="i686-w64-mingw32-gcc" +CFLAGS="-Wall -D_REENTRANT -I${SRC}" +DIST="${JOEY}/dist/windows/x86" +INSTALLED="${JOEY}/SDL2/installed/windows/x86/lib" +doBuild + +CC="o32-clang" +CFLAGS="-Wall -D_REENTRANT -I${SRC}" +DIST="${JOEY}/dist/macos/x86" +INSTALLED="${JOEY}/SDL2/installed/macos/x86/lib" +doBuild + +CC="o64-clang" +CFLAGS="-Wall -D_REENTRANT -I${SRC}" +DIST="${JOEY}/dist/macos/x64" +INSTALLED="${JOEY}/SDL2/installed/macos/x64/lib" +doBuild + +rm -rf build diff --git a/joeylib.pro b/joeylib.pro new file mode 100644 index 0000000..1cc8248 --- /dev/null +++ b/joeylib.pro @@ -0,0 +1,45 @@ +TEMPLATE = app +CONFIG += console +CONFIG -= \ + app_bundle \ + qt + +QMAKE_CFLAGS += \ + -I$$PWD/src \ + -D_REENTRANT + +HEADERS += \ + src/joey.h \ + src/stddclmr.h + +SOURCES += \ + src/joey.c \ + src/jPC.c \ + src/test.c + +OTHER_FILES += \ + src/jIIgs.c \ + src/jIIgs.asm \ + src/jIIgs.macro \ + build-IIgs.sh \ + build-PC.sh + +SDL_LIBS = \ + -L$$PWD/lib/linux/x64 \ + -Wl,-rpath,$$PWD/lib/linux/x64 \ + -Wl,--enable-new-dtags \ + -lSDL2 \ + -Wl,--no-undefined \ + -lm \ + -ldl \ + -lpthread \ + -lrt + +SDL_MIXER_LIBS = \ + -L$$PWD/lib/linux/x64 \ + -lSDL2_mixer \ + -lmikmod + +LIBS += \ + $$SDL_LIBS \ + $$SDL_MIXER_LIBS diff --git a/src/font.sta b/src/font.sta new file mode 100644 index 0000000000000000000000000000000000000000..f488f74c7c15036fd5426f616ca4dfa148d90bc9 GIT binary patch literal 32036 zcmeH~v2q+o3`93!a>;&^|G-tYV*Q$9ecdIB&;1&}%-)GC(JDFXC2waIXf%3yW{<_c z{{G`Qj@$9i@lC_+_UG-}?aSNOx9@L{w=d_{^ZWTY1vrlLuHX8p`EfrUk4hXGnSOME zQ@!zBb3PufpRu@xKsj9}oZ!1&SkF(Tsav%MU-Xe1tq7xc>-QY|z8+vF@A*MJCC@*r zgd|sUx<=YIn)$SCaAP03DzevA-d&!Hz3;H%6+N3(2H?f;Oy5a+$@pvHsvk@-@U+;f zPmzg2llHrMDlKW;tlhk-?UA^Si2XBAn>Ordv?j)duh4D^zV>p~WUby~Xua@)qe7P% zeD?`)Ti>SNLTh4atxq)s)TgRh>*SftMRxq_Qfm?(RLxE8`IIp_C;GuVS4D;G`HHs^ zCHXCk@7=}mJ#Uc9J$KK;%BH70TQFj)m$B981YsY@N`!vb)g?eYWj*r4o?ub4L zbi7guxTkjq0_sr+hPn5)-Z68m#-E&AwSKZ>!G59E9-^K1&OhbA#P_;peS<4R)#%Qd zf`@BHQSoY3@Yn9{QX3_od+#55EB(#3y0`Y))Ezw*s#YawmZ7^j{blIC0rg07X1&ks zwnJ7Tf;w`haY{+&d+(K&7t(k2X%rRLJgJN?9+g8NO^vk+O;pP zuR?K_R6Qy`{#7M8i%@du;~jt1)?5OaPg243`53NhG(2FPp@-{!)K_PZ^%`=~k0-?2 z3+nNNSf5gJ(gOc{8y^mTxs8tEcD0*~ZZ|b+*Rg-SqUpuCbj)k5b6>jL(`cT})gvlh zk7mvZaR57S`}Dle$KViK9ar6GS-j?>|Nn5ge<}Eg-Di4|zfDUWfT8t>ZMC`OF9z(0 zPHA1n8fvHfD4eUkFTV8wTi};TulpssALk`kgsx8bUQ@RieQY5+-&gH_MWziqKj*=V zYyM`VnnrUz7@tn2&mA4Tla6_zJfG-7fLcQC*#`j*G-l-IVJz zsU~2UB8V8)i8F!FlU)4$fZ^qp+7;BCav{V>eDslrwsAea=p(An&*ar| zo9RmUq>nS({cMkox>_YQ+rMYF?VmAd(g!+IP5Py1Kh{bw>vcu1ODy_ft=2cB|4pCk zMS;G$Oly76ckqLORZu%Dc+SUVCNiz}u`!oDH7U>fT=$oFb6%K@c1Ca&Fs(+Dd1rKe zb2(kgR&|N8rmfF-$5g_o&wKj7b=9SH2lamZT;A+lu`IeOv2jV~+hy!sT4#a^t5C`Hw9EbyPAP&TV zI1mToKpcnzaUc%FfjAHc;y@gT192b@#DO>v2jV~+hy!sT4#a^t5C`Hw9EbyPAP&TV zI1mToKpcnzaUc%FfjAHc;y@gT192b@#DO>v2jV~+hy!sT4#a^t5C`Hw9EbyPAP&TV RI1mToKpcnzao|%9{0pkhJ)r;q literal 0 HcmV?d00001 diff --git a/src/jIIgs.asm b/src/jIIgs.asm new file mode 100644 index 0000000..32b6280 --- /dev/null +++ b/src/jIIgs.asm @@ -0,0 +1,721 @@ +;---------------------------------------- +; JoeyLib +; Copyright (C) 2018 Scott Duensing +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +;---------------------------------------- + + mcopy 13:ORCAInclude:m16.ORCA + mcopy jIIgs.macro + case on + +SHRShad gequ $012000 + +savedDBR data ; Storage for Data Bank Register + ds 2 ; 16 bits + end ; Used by jIIgs.macro + +vblDBR data ; Storage for Data Bank Register during VBL Interrupt + ds 2 ; 16 bits + end + +ScanTable data + ds 400 ; 400 bytes for scanline offsets + end + +VblRate data + ds 2 ; Either 5 or 6 depending on PAL or NTSC. +VblTime ds 2 ; Integer Counter + end + +;---------------------------------------- +; Blit an 8x8 block from off-screen to back buffer. +;---------------------------------------- +asmB88 start + +t equ 1 + + jsubroutine (4:p,2:cx1,2:cy1,2:cx2,2:cy2),2 + using ScanTable + + phb ; Push our current data bank onto the stack + +; Find offset into tile memory + clc + lda cx1 ; Multiply cx1 by 4 to get offset (two pixels per byte) + asl a + asl a + sta t + clc + lda cy1 ; Multiply cy1 by 16 to get index into scanline table + asl a + asl a + asl a + asl a ; y1 is now in the accumulator + clc + tax + lda >ScanTable,x + adc t ; Add t to scanline offset + tay ; Offset to start of tile +; Find offset into shadow SHR memory + clc + lda cx2 ; Multiply cx1 by 4 to get offset (two pixels per byte) + asl a + asl a + sta t + clc + lda cy2 ; Multiply cy1 by 16 to get index into scanline table + asl a + asl a + asl a + asl a ; y2 is now in the accumulator + clc + tax + lda >ScanTable,x + adc t ; Add t to scanline offset + tax ; Offset to start of screen memory + + sei ; Disable interrupts while we change data banks + pea $0101 ; Push Effective Address (our new data bank) always 16 bits + plb ; Pull data bank from stack (data bank now $01) + plb ; Do it twice because it's only an 8 bit operation + +; Row 1 + lda [p],y ; Load 4 pixels from SHA data + sta |SHRShad,x ; Store 4 pixels into screen + inx ; Move to next pixel quad + inx + iny + iny + lda [p],y ; Load 4 pixels from SHA data + sta |SHRShad,x ; Store 4 pixels into screen +; Move to row 2 + txa + adc #158 ; Next line + tax + tya + adc #158 ; Next line + tay +; Row 2 + lda [p],y ; Load 4 pixels from SHA data + sta |SHRShad,x ; Store 4 pixels into screen + inx ; Move to next pixel quad + inx + iny + iny + lda [p],y ; Load 4 pixels from SHA data + sta |SHRShad,x ; Store 4 pixels into screen +; Move to row 3 + txa + adc #158 ; Next line + tax + tya + adc #158 ; Next line + tay +; Row 3 + lda [p],y ; Load 4 pixels from SHA data + sta |SHRShad,x ; Store 4 pixels into screen + inx ; Move to next pixel quad + inx + iny + iny + lda [p],y ; Load 4 pixels from SHA data + sta |SHRShad,x ; Store 4 pixels into screen +; Move to row 4 + txa + adc #158 ; Next line + tax + tya + adc #158 ; Next line + tay +; Row 4 + lda [p],y ; Load 4 pixels from SHA data + sta |SHRShad,x ; Store 4 pixels into screen + inx ; Move to next pixel quad + inx + iny + iny + lda [p],y ; Load 4 pixels from SHA data + sta |SHRShad,x ; Store 4 pixels into screen +; Move to row 5 + txa + adc #158 ; Next line + tax + tya + adc #158 ; Next line + tay +; Row 5 + lda [p],y ; Load 4 pixels from SHA data + sta |SHRShad,x ; Store 4 pixels into screen + inx ; Move to next pixel quad + inx + iny + iny + lda [p],y ; Load 4 pixels from SHA data + sta |SHRShad,x ; Store 4 pixels into screen +; Move to row 6 + txa + adc #158 ; Next line + tax + tya + adc #158 ; Next line + tay +; Row 6 + lda [p],y ; Load 4 pixels from SHA data + sta |SHRShad,x ; Store 4 pixels into screen + inx ; Move to next pixel quad + inx + iny + iny + lda [p],y ; Load 4 pixels from SHA data + sta |SHRShad,x ; Store 4 pixels into screen +; Move to row 7 + txa + adc #158 ; Next line + tax + tya + adc #158 ; Next line + tay +; Row 7 + lda [p],y ; Load 4 pixels from SHA data + sta |SHRShad,x ; Store 4 pixels into screen + inx ; Move to next pixel quad + inx + iny + iny + lda [p],y ; Load 4 pixels from SHA data + sta |SHRShad,x ; Store 4 pixels into screen +; Move to row 8 + txa + adc #158 ; Next line + tax + tya + adc #158 ; Next line + tay +; Row 8 + lda [p],y ; Load 4 pixels from SHA data + sta |SHRShad,x ; Store 4 pixels into screen + inx ; Move to next pixel quad + inx + iny + iny + lda [p],y ; Load 4 pixels from SHA data + sta |SHRShad,x ; Store 4 pixels into screen + + plb ; Pull original data bank from stack + cli ; Restore interrupts + + jreturn + end + +;---------------------------------------- +; Set Border Color +;---------------------------------------- +asmBorder start + jsubroutine (2:c) + short m + lda $E0C034 + and #$F0 + ora c + sta $E0C034 + long m + jreturn + end + +;---------------------------------------- +; Returns the color of a given point +; +; Psychotically commented to help learn ASM :-) +;---------------------------------------- +asmGetPoint start + +temp1 equ 1 + + jsubroutine (2:Xp,2:Yp),2 + using ScanTable + + lda Yp ; Load accumulator with Y location + asl a ; Shift accumulator left (multiply by 2) for word offset into table + tax ; Transfer accumulator to x register + lda >ScanTable,x ; Look up scan line address offset + sta temp1 ; Store accumulator (row offset into SHR memory) into temp1 + clc ; Clear carry flag + lda Xp ; Place X position in accumulator + lsr a ; Logical Shift Right (divide by 2, two pixels per byte) - bit shifted off the end goes into carry flag + php ; Save our carry flag for later! + clc ; Clear carry + adc temp1 ; Add X position to SHR address with carry - a now contains address of color pair we want + plp ; Restore our carry flag + tax ; Transfer accumulator to x register + lda SHRShad,x ; Load pixel pair into accumulator + bcc GEven ; Branch if Carry Clear - Determines Even/Odd pixel + and #$F ; AND accumulator with $F to mask off unwanted pixel + bra GBoth + +GEven and #$F0 ; AND accumulator with $F0 to mask off unwanted pixel + lsr a ; Shift the result down a nibble + lsr a + lsr a + lsr a + +GBoth sta temp1 ; Store accumulator in temp1 + + jreturn 2:temp1 + end + +;---------------------------------------- +; Return the current 1/10th second timer value +;---------------------------------------- +asmGetVbl start + using VblRate + lda >VblTime + rtl + end + +;---------------------------------------- +; Turns off SHR Graphics +;---------------------------------------- +asmGrOff start + jsubroutine + short m + lda $E1C029 + and #$7F + sta $E1C029 + long m + jreturn + end + +;---------------------------------------- +; Turns on SHR Graphics +;---------------------------------------- +asmGrOn start + jsubroutine + short m + lda $E1C029 + ora #$C1 + sta $E1C029 + long m + jreturn + end + +;---------------------------------------- +; Reads controller axis +;---------------------------------------- +asmJoy start + +count equ 1 + +strobe equ $00C070 ; Paddle timer reset +paddle equ $00C064 ; First paddle address + + jsubroutine (2:which),2 + + lda which ; Which paddle/axis do we want? + tax + php + sei ; Disable interrupts + stz count ; Reset paddle value counter + short m + lda >strobe ; Start timer +pdlLbl1 nop ; Delay some + nop + nop + nop + inc count ; Increment paddle value counter + beq pdlLbl2 ; If we loop back to zero, Z is set and we branch + lda >paddle,x ; Check paddle value + bmi pdlLbl1 ; High bit not set yet +pdlLbl2 dec count ; Decrement one. Makes timeouts equal 255. + long m + plp ; Restore interrupt setting + + jreturn 2:count + end + +;---------------------------------------- +; Plots a point in a given color +; +; Psychotically commented to help learn ASM :-) +;---------------------------------------- +asmPoint start + +temp1 equ 1 +temp2 equ 3 +temp3 equ 5 + + jsubroutine (2:C,2:Xp,2:Yp),6 + using ScanTable + + lda Xp ; Place X position in accumulator + lsr a ; Logical Shift Right (divide by 2) - bit shifted off the end goes into carry flag + sta temp1 ; Store accumulator in temp1 + bcc PEven ; Branch if Carry Clear - Determines Even/Odd pixel + +POdd lda #$FFF0 ; Load accumulator with pixel mask + sta temp2 ; Store accumulator in temp2 + lda C ; Load accumulator with color + and #$F ; AND accumulator with $F + bra PBoth ; Branch Always to PBoth + +PEven lda #$FF0F ; Load accumulator with pixel mask + sta temp2 ; Store accumulator in temp2 + lda C ; Load accumulator with color + and #$F0 ; AND accumulator with $F0 + +PBoth sta temp3 ; Store accumulator in temp3 + lda Yp ; Load accumulator with Y location + asl a ; Shift accumulator left (multiply by 2) + tax ; Transfer accumulator to x register + lda >ScanTable,x ; Look up scan line address offset + adc temp1 ; Add X position to address with carry + tax ; Transfer accumulator to x + + lda SHRShad,x ; Load existing pixels into accumulator + and temp2 ; AND accumulator with temp2 + ora temp3 ; OR accumulator with temp3 + sta SHRShad,x ; Store updated pixels back into memory + + jreturn + end + +;---------------------------------------- +; Turn SHR Shadowing Off +;---------------------------------------- +asmShOff start + jsubroutine + short m + lda $E0C035 + ora #$08 + sta $E0C035 + long m + jreturn + end + +;---------------------------------------- +; Turn SHR Shadowing On +;---------------------------------------- +asmShOn start + jsubroutine + short m + lda $E0C035 + and #$F7 + sta $E0C035 + long m + jreturn + end + +;---------------------------------------- +; "PEI Slam" shadowed SHR display to the +; actual display. Copies all 200 lines, +; the palette, and SCBs. +; +; Basically stolen from https://www.yumpu.com/en/document/view/23761498/code-secrets-of-wolfenstein-3d-iigs-kansasfest +;---------------------------------------- +asmSlam start + + jsubroutine + + php ; Push Processor Status Register + tdc ; Transfer Direct Register to C Accumulator + sta >DataPtr ; Store Accumulator in DataPtr + tsc ; Transfer Stack Pointer to C Accumulator + sta >StackPtr ; Store Accumulator in StackPtr + + sei ; Disable interrupts + short m + sta >$00C005 ; Switch to AUX + sta >$00C003 + long m + + ldy #$2000 ; Point to start of SHR + +peiChunk ldx #7 ; Chunks before interrupts + +peiPage tya + tcd ; Move DP + + clc + adc #$FF ; Point to top of page + tcs ; Move stack + + inc a + tay ; Keep + 0x100 for later + + pei $FE ; "Slam" a single page + pei $FC + pei $FA + pei $F8 + pei $F6 + pei $F4 + pei $F2 + pei $F0 + pei $EE + pei $EC + pei $EA + pei $E8 + pei $E6 + nop + pei $E4 + pei $E2 + pei $E0 + pei $DE + pei $DC + pei $DA + pei $D8 + pei $D6 + pei $D4 + pei $D2 + pei $D0 + pei $CE + pei $CC + nop + pei $CA + pei $C8 + pei $C6 + pei $C4 + pei $C2 + pei $C0 + pei $BE + pei $BC + pei $BA + pei $B8 + pei $B6 + pei $B4 + pei $B2 + nop + pei $B0 + pei $AE + pei $AC + pei $AA + pei $A8 + pei $A6 + pei $A4 + pei $A2 + pei $A0 + pei $9E + pei $9C + pei $9A + pei $98 + nop + pei $96 + pei $94 + pei $92 + pei $90 + pei $8E + pei $8C + pei $8A + pei $88 + pei $86 + pei $84 + pei $82 + pei $80 + nop + pei $7E + pei $7C + pei $7A + pei $78 + pei $76 + pei $74 + pei $72 + pei $70 + pei $6E + pei $6C + pei $6A + pei $68 + pei $66 + nop + pei $64 + pei $62 + pei $60 + pei $5E + pei $5C + pei $5A + pei $58 + pei $56 + pei $54 + pei $52 + pei $50 + pei $4E + nop + pei $4C + pei $4A + pei $48 + pei $46 + pei $44 + pei $42 + pei $40 + pei $3E + pei $3C + pei $3A + pei $38 + pei $36 + pei $34 + nop + pei $32 + pei $30 + pei $2E + pei $2C + pei $2A + pei $28 + pei $26 + pei $24 + pei $22 + pei $20 + pei $1E + pei $1C + pei $1A + nop + pei $18 + pei $16 + pei $14 + pei $12 + pei $10 + pei $0E + pei $0C + pei $0A + pei $08 + pei $06 + pei $04 + pei $02 + pei $00 + + cpy #$A000 ; Did we copy everything? + bge peiEnd ; Yep! + + dex + bmi peiInts ; Do we need to process interrupts? + + brl peiPage + +peiInts short m + sta >$00C004 ; Switch to MAIN + sta >$00C002 + long m + lda >StackPtr ; Load original Stack Pointer into Accumulator + tcs ; Transfer Accumulator to Stack Pointer (Restores original stack) + lda >DataPtr ; Load original Direct Pointer into Accumulator + tcd ; Transfer Accumulator to Direct Pointer (Restores direct page) + + cli ; Go Interrupts! Go! + sei ; Stop interrupts + + short m + sta >$00C005 ; Switch to AUX + sta >$00C003 + long m + + brl peiChunk + +peiEnd short m + sta >$00C004 ; Switch to MAIN + sta >$00C002 + long m + lda >StackPtr + tcs ; Restore stack + lda >DataPtr + tcd ; Restore DP + cli ; Enable Interrupts + + plp ; Restore processor register + + jreturn + +StackPtr ds 2 ; Space for SP +DataPtr ds 2 ; Space for DP + + end + +;---------------------------------------- +; Start up assembly routines +; +; Builds a table of scanline offsets +;---------------------------------------- +asmStart start + jsubroutine (2:id,2:h) + using ScanTable + using VblHdr + using VblRate + + ldx #0 ; Load 0 into x register + lda #0 ; Load 0 into accumulator + clc ; Clear carry flag +STL sta ScanTable,x ; Store accumulator in ScanTable+x + adc #160 ; Add 160 to accumulator + inx ; Increment x + inx ; Increment x + cpx #400 ; Compare x to 400 (200 scanlines) + bcc STL ; Repeat Scan Table Loop until we do all 200 lines + + lda h ; Store refresh rate / 10 in VblCount and VblRate + sta >VblCount + sta >VblRate + + lda #42 ; Reset timer + sta >VblTime + +;ph4 #VblHdr ; Start VBL interrupt task +;ldx #$1203 ;_SetHeartBeat +;jsl $E10000 + + jreturn + end + +;---------------------------------------- +; Shut down assembly routines +;---------------------------------------- +asmStop start + jsubroutine + +;ph4 #VblHdr ; Stop VBL interrupt task +;ldx #$1303 ;_DelHeartBeat +;jsl $E10000 + + jreturn + end + +;---------------------------------------- +; Increment time counter using VBLs every 1/10 second +;---------------------------------------- +VblHdr data + dc i4'0' ; Space for task pointer +VblCount dc i2'1' ; How many VBLs between calls (1 only on initial call) + dc i2'$A55A' ; Task signature + end + +VblTask start + using VblHdr + using VblRate + using vblDBR + long m ; 16 bit Accumulator + phb ; Push Data Bank Register + phb ; Push Data Bank Register + pla ; Pull Accumulator + sta >vblDBR ; Store Accumulator in savedDBR (Data Bank Register) + phk ; Push K (Program Bank Register) + plb ; Pull Data Bank Register + lda >VblRate ; Reset counter + sta >VblCount + lda >VblTime ; Increment timer + inc a + sta >VblTime + lda >vblDBR ; Load our saved Data Bank Register + pha ; Push the C Accumulator + plb ; Pull Data Bank Register + plb ; Pull Data Bank Register + short m ; Back to 8 bit Accumulator + rtl + end diff --git a/src/jIIgs.c b/src/jIIgs.c new file mode 100644 index 0000000..9f65080 --- /dev/null +++ b/src/jIIgs.c @@ -0,0 +1,332 @@ +/* + * JoeyLib + * Copyright (C) 2018 Scott Duensing + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. +*/ + + +#include + +#include +#include +#include +#include +#undef false +#undef true + + +#define JOEY_LIBRARY +#include "joey.h" + + +#ifdef JOEY_DEBUG +#include +#endif + + +typedef struct { + char length; + char text[255]; +} _jlPascalStringT; + + +extern void asmBorder(int c); +extern void asmGrOff(void); +extern void asmGrOn(void); +extern int asmJoy(int which); +extern void asmShOff(void); +extern void asmShOn(void); +extern void asmSlam(void); +extern void asmStart(int myID, int hertzBy10); +extern void asmStop(void); + + +extern pascal void NTStartUp(Word) inline(0x02DD, dispatcher); +extern pascal void NTShutDown(void) inline(0x03DD, dispatcher); +extern pascal void NTLoadOneMusic(Pointer) inline(0x09DD, dispatcher); +extern pascal void NTPlayMusic(Word) inline(0x0ADD, dispatcher); +extern pascal Word NTGetPlayingMusic(void) inline(0x10DD, dispatcher); +extern pascal void NTPauseMusic(void) inline(0x13DD, dispatcher); +extern pascal void NTContinueMusic(void) inline(0x14DD, dispatcher); + + +char _jlKeyCheck(char key); + + +static byte *KEYBOARD = (byte *)0x00C000; +static byte *KEYSTROBE = (byte *)0x00C010; +static byte *BUTTON0 = (byte *)0x00C061; +static byte *BUTTON1 = (byte *)0x00C062; +static jlPixelPairT *SHRPIXELS = (jlPixelPairT *)0x012000; // Shadow of 0xE12000 +static byte *SHRSCB = (byte *)0x019D00; // Shadow of 0xE19D00 +static jlColorT *SHRCOLORS = (jlColorT *)0x019E00; // Shadow of 0xE19E00 +static byte *BORDER = (byte *)0xE0C034; + + +static byte _jlBorderSaved; +static int _jlMyID; +static int _jlMemID; +static int _jlMusicMemID; +static Handle _jlSHRShadowHandle; +static int _jlHertz; + + +#ifdef JOEY_DEBUG +#define JOEY_CHECK_TOOL_ERROR(w) if (_toolErr) _jlDieWithToolError(w, _toolErr); +void _jlDieWithToolError(const char *what, int err) { + char message[256]; + sprintf(message, "Tool Error %04X %s", err, what); + jlUtilDie(message); +} + +void _jlDebugBorder(jlBorderColorsE color) { + jlDisplayBorder(color); + asmBorder(_jlBorderColor); + //*BORDER = *BORDER & 0xF0 | _jlBorderColor; +} +#else +#define JOEY_CHECK_TOOL_ERROR(w) +#endif + + +void jlDisplayPresent(void) { + asmShOn(); + asmSlam(); + asmShOff(); + asmBorder(_jlBorderColor); +} + + +void jlDrawClear(void) { + memset(SHRPIXELS, _jlDrawColorNibbles, 32000); +} + + +int jlGameGetAxis(byte which) { + return asmJoy((int)which) - 128; +} + + +bool jlGameGetButton(byte which) { + if (which > 0) return (bool)(*BUTTON1 > 127); + return (bool)(*BUTTON0 > 127); +} + + +char _jlKeyCheck(char key) { + if ((key < 8) || (key > 127)) { + key = 0; + } else { + // Map LF to CR + if (key == 10) { + key = 13; + } + // Map DEL to BS + if (key == 127) { + key = 8; + } + // Is this outside the range we care about? + if ((key < ' ') || (key > '~')) { + if ((key != 8) && (key != 13) && (key != 27)) { + key = 0; + } + } + } + return key; +} + + +bool jlKeyPressed(void) { + bool result = false; + // On other platforms we should call jlIdle() here. + if ((*KEYBOARD & 0x80) != 0) { + result = (_jlKeyCheck(*KEYBOARD & 0x7F) != 0); + } + return result; +} + + +char jlKeyRead(void) { + // On other platforms we should call jlIdle() here. + + // Reset keyboard strobe + *KEYSTROBE = 0; + + return _jlKeyCheck(*KEYBOARD & 0x7F); +} + + +void jlPaletteSet(byte index, byte r, byte g, byte b) { + SHRCOLORS[index].r = r; + SHRCOLORS[index].g = g; + SHRCOLORS[index].b = b; +} + + +void jlSoundFree(jlSoundT *sound) { + //***TODO*** +} + + +bool jlSoundIsPlaying(jlSoundT *sound) { + //***TODO*** +} + + +bool _jlSoundLoad(jlSoundT **sound, char *filename) { + //***TODO*** +} + + +void jlSoundMusicContinue(void) { + NTContinueMusic(); +} + + +bool jlSoundMusicIsPlaying(void) { + return (NTGetPlayingMusic() != 0xFFFF); +} + + +void jlSoundMusicPause(void) { + NTPauseMusic(); +} + + +void jlSoundMusicPlay(char *name) { + _jlPascalStringT file; + strcpy(file.text, name); + file.length = strlen(name); + NTStartUp(_jlMusicMemID); + JOEY_CHECK_TOOL_ERROR("NTStartup") + NTLoadOneMusic((Pointer)&file); + JOEY_CHECK_TOOL_ERROR("NTLoadOneMusic") + NTPlayMusic(false); + JOEY_CHECK_TOOL_ERROR("NTPlayMusic") +} + + +void jlSoundMusicStop(void) { + NTShutDown(); + JOEY_CHECK_TOOL_ERROR("NTShutDown") +} + + +void jlSoundPlay(jlSoundT *sound) { + //***TODO*** +} + + +bool _jlStaCreate(jlStaT **sta) { + jlStaT *t = (jlStaT *)jlMalloc(sizeof(jlStaT)); + if (t != NULL) { + memset(t, 0, sizeof(jlStaT)); + t->id[0] = 'S'; + t->id[1] = 'T'; + t->id[2] = 'A'; + t->version = 0; + memcpy(t->palette, SHRCOLORS, sizeof(t->palette)); + memcpy(t->pixels, SHRPIXELS, sizeof(t->pixels)); + *sta = t; + return true; + } + return false; +} + + +void jlStaDisplay(jlStaT *sta) { + memcpy(SHRCOLORS, sta->palette, sizeof(sta->palette)); + memcpy(SHRPIXELS, sta->pixels, sizeof(sta->pixels)); +} + + +void jlUtilIdle(void) { + // No need to pump the message loop on the IIgs +} + + +bool jlUtilMustExit(void) { + return false; +} + + +void jlUtilShutdown(void) { + // Clear the display. + jlDrawColor(0); + jlDrawClear(); + + // Restore Border + jlDisplayBorder((jlBorderColorsE)_jlBorderSaved); + jlDisplayPresent(); + + // SHR Off + asmGrOff(); + + asmStop(); + + // Shutdown tools + UnloadOneTool(0xDD); // NinjaTracker + MTShutDown(); + DisposeAll(_jlMemID); + MMShutDown(_jlMyID); + TLShutDown(); + + jlUtilDie("Clean Exit."); +} + + +void jlUtilStartup(char *appName) { + + (void)appName; + + // Start up neded tools + TLStartUp(); + _jlMyID = MMStartUp(); + _jlMemID = _jlMyID | 0x0100; // Set aux ID to 1. + _jlMusicMemID = _jlMyID | 0x0200; // Set music ID. + MTStartUp(); + LoadOneTool(0xDD, 0); // NinjaTracker + JOEY_CHECK_TOOL_ERROR("LoadOneTool") + + // Reserve shadow area for SHR + _jlSHRShadowHandle = NewHandle((LongWord)0x8000, (Word)_jlMemID, (Word)(attrLocked + attrFixed + attrBank + attrAddr), (Pointer)0x012000); + + // Start assembly module + _jlHertz = (int)ReadBParam((Word)0x1D) == 0 ? 60 : 50; // Is this a PAL or NTSC machine? + asmStart(_jlMyID, _jlHertz / 10); + + // SHR on + asmGrOn(); + + // If you work out the Screen Conrol Bytes, everything we want is zero. + // So we just memset all 200 of them. + memset(SHRSCB, 0, 200); + + // Hide border. + _jlBorderSaved = *BORDER & 0x0F; + jlDisplayBorder(BORDER_BLACK); + + jlPaletteDefault(); + + // Clear the display. + jlDrawColor(0); + jlDrawClear(); + jlDrawColor(15); + + jlDisplayPresent(); +} diff --git a/src/jIIgs.macro b/src/jIIgs.macro new file mode 100644 index 0000000..0f52cc7 --- /dev/null +++ b/src/jIIgs.macro @@ -0,0 +1,119 @@ +; +; Based on M16.cc from ORCA/C +; + + MACRO +&lab jsubroutine &parms,&work +&lab anop + using savedDBR ; ADDED + aif c:&work,.a + lclc &work +&work setc 0 +.a + gbla &totallen + gbla &worklen +&worklen seta &work +&totallen seta 0 + aif c:&parms=0,.e + lclc &len + lclc &p + lcla &i +&i seta 1 +.b +&p setc &parms(&i) +&len amid &p,2,1 + aif "&len"=":",.c +&len amid &p,1,2 +&p amid &p,4,l:&p-3 + ago .d +.c +&len amid &p,1,1 +&p amid &p,3,l:&p-2 +.d +&p equ &totallen+4+&work +&totallen seta &totallen+&len +&i seta &i+1 + aif &i<=c:&parms,^b +.e + tsc + aif &work=0,.f + sec + sbc #&work + tcs +.f + phd ; Push Direct Register + tcd ; Transfer C Accumulator to Direct Register + +; === START OF OUR MODIFICATION === + phb ; Push Data Bank Register + phb ; Push Data Bank Register + pla ; Pull Accumulator + sta >savedDBR ; Store Accumulator in savedDBR (Data Bank Register) + phk ; Push K (Program Bank Register) + plb ; Pull Data Bank Register +; === END OF OUR MODIFICATION === + + mend + + + MACRO +&lab jreturn &r +&lab anop + +; === START OF OUR MODIFICATION === + using savedDBR + lda >savedDBR ; Load our saved Data Bank Register + pha ; Push the C Accumulator + plb ; Pull Data Bank Register + plb ; Pull Data Bank Register +; === END OF OUR MODIFICATION === + + lclc &len + aif c:&r,.a + lclc &r +&r setc 0 +&len setc 0 + ago .h +.a +&len amid &r,2,1 + aif "&len"=":",.b +&len amid &r,1,2 +&r amid &r,4,l:&r-3 + ago .c +.b +&len amid &r,1,1 +&r amid &r,3,l:&r-2 +.c + aif &len<>2,.d + ldy &r + ago .h +.d + aif &len<>4,.e + ldx &r+2 + ldy &r + ago .h +.e + aif &len<>10,.g + ldy #&r + ldx #^&r + ago .h +.g + mnote 'Not a valid return length',16 + mexit +.h + aif &totallen=0,.i + lda &worklen+2 + sta &worklen+&totallen+2 + lda &worklen+1 + sta &worklen+&totallen+1 +.i + pld + tsc + clc + adc #&worklen+&totallen + tcs + aif &len=0,.j + tya +.j + rtl + mend diff --git a/src/jPC.c b/src/jPC.c new file mode 100644 index 0000000..8f5b620 --- /dev/null +++ b/src/jPC.c @@ -0,0 +1,546 @@ +/* + * JoeyLib + * Copyright (C) 2018 Scott Duensing + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include + +#include "SDL2/SDL.h" +#include "SDL2/SDL_mixer.h" + +#define JOEY_LIBRARY +#include "joey.h" + + +static SDL_Window *_jlWindow = NULL; +static SDL_Renderer *_jlRenderer = NULL; +static jlStaT *_jlBackingStore = NULL; // 4 bit representation +static SDL_Texture *_jlTexture = NULL; // Video card representation in ARGB +static SDL_PixelFormat *_jlPixelFormat = NULL; // Pixel format of _jlTexture +static bool _jlIsRunning = true; +static int _jlNumKeysDown = 0; +static char _jlLastKey = 0; +static Mix_Music *_jlMusicHandle = NULL; +static SDL_GameController **_jlControllers = NULL; +static int _jlControllerCount = 0; +static unsigned int _jlTimerValue = 0; +static SDL_TimerID _jlTimerId = 0; + + +Uint32 _jlUtilTimer(Uint32 interval, void *param); + + +/* +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + +Uint32 _jlGetPixel(SDL_Surface *surface, int x, int y) { + + int bpp = surface->format->BytesPerPixel; + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; + + switch(bpp) { + case 1: + return *p; + break; + + case 2: + // Generates cast increases required alignment of target type [-Wcast-align] warning. Harmless on x86. + return *(Uint16 *)p; + break; + + case 3: + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + return (Uint32)(p[0] << 16 | p[1] << 8 | p[2]); + else + return (Uint32)(p[0] | p[1] << 8 | p[2] << 16); + break; + + case 4: + // Generates cast increases required alignment of target type [-Wcast-align] warning. Harmless on x86. + return *(Uint32 *)p; + break; + + default: + return 0; + } +} + + +void _jlPutPixel(SDL_Surface *surface, int x, int y, Uint32 pixel) { + + int bpp = surface->format->BytesPerPixel; + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; + + switch(bpp) { + case 1: + *p = (Uint8)pixel; + break; + + case 2: + *(Uint16 *)p = (Uint16)pixel; + break; + + case 3: + if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { + p[0] = (pixel >> 16) & 0xff; + p[1] = (pixel >> 8) & 0xff; + p[2] = pixel & 0xff; + } else { + p[0] = pixel & 0xff; + p[1] = (pixel >> 8) & 0xff; + p[2] = (pixel >> 16) & 0xff; + } + break; + + case 4: + *(Uint32 *)p = pixel; + break; + } +} + +#pragma GCC diagnostic pop +*/ + + +void jlDisplayPresent(void) { + // Render 4 bit copy to proper pixel format. + // This extra step preserves palette effects. + //***TODO*** Fake border colors on PC + int i = 0; + int j = 0; + int pitch = 0; + void *pixelData = NULL; + jlUtilIdle(); + SDL_LockTexture(_jlTexture, NULL, &pixelData, &pitch); + Uint32 *pixels = (Uint32 *)pixelData; + for (int y=0; y<200; y++) { + for (int x=0; x<160; x++) { + // We decode this R/L instead of L/R for some reason. NO idea why yet. Endians? + pixels[i++] = SDL_MapRGBA(_jlPixelFormat, + _jlBackingStore->palette[_jlBackingStore->pixels[j].r].r * 16, + _jlBackingStore->palette[_jlBackingStore->pixels[j].r].g * 16, + _jlBackingStore->palette[_jlBackingStore->pixels[j].r].b * 16, + 255); + pixels[i++] = SDL_MapRGBA(_jlPixelFormat, + _jlBackingStore->palette[_jlBackingStore->pixels[j].l].r * 16, + _jlBackingStore->palette[_jlBackingStore->pixels[j].l].g * 16, + _jlBackingStore->palette[_jlBackingStore->pixels[j].l].b * 16, + 255); + j++; + } + } + SDL_UnlockTexture(_jlTexture); + + SDL_RenderCopy(_jlRenderer, _jlTexture, NULL, NULL); + SDL_RenderPresent(_jlRenderer); +} + + +void jlDrawBlit8x8(jlStaT *sta, int cx1, int cy1, int cx2, int cy2) { + int o1; + int o2; + int x; + int y; + + o1 = (cy1 * 8 * 160) + (cx1 * 4); + o2 = (cy2 * 8 * 160) + (cx2 * 4); + + for (y=0; y<8; y++) { + for (x=0; x<4; x++) { + _jlBackingStore->pixels[o2++] = sta->pixels[o1++]; + } + o1 += 156; + o2 += 156; + } +} + + +void jlDrawClear(void) { + memset(_jlBackingStore->pixels, _jlDrawColorNibbles, 32000); +} + + +byte jlDrawGetPixel(int x, int y) { + int p = x / 2 + y * 160; + return (jlUtilIsOdd(x) ? _jlBackingStore->pixels[p].l : _jlBackingStore->pixels[p].r); +} + + +void jlDrawPoint(int x, int y) { + jlPixelPairT *pixelPair = _jlBackingStore->pixels + (y * 160) + (x / 2); + if (jlUtilIsOdd(x)) { + pixelPair->l = _jlDrawColor; + } else { + pixelPair->r = _jlDrawColor; + } +} + + +int jlGameGetAxis(byte which) { + SDL_GameControllerAxis axis; + short int unscaled; + short int max = SHRT_MIN; + short int min = SHRT_MAX; + int x; + + jlUtilIdle(); + + if (which == 0) { + axis = SDL_CONTROLLER_AXIS_LEFTX; + } else { + axis = SDL_CONTROLLER_AXIS_LEFTY; + } + + for (x=0; x<_jlControllerCount; x++) { + unscaled = SDL_GameControllerGetAxis(_jlControllers[x], axis); + if (unscaled > 0) { + if (unscaled > max) { + max = unscaled; + } + } else { + if (unscaled < min) { + min = unscaled; + } + } + } + + return (-min > max ? min : max) / 256; +} + + +bool jlGameGetButton(byte which) { + SDL_GameControllerButton button; + int x; + bool pressed = false; + + jlUtilIdle(); + + if (which == 0) { + button = SDL_CONTROLLER_BUTTON_A; + } else { + button = SDL_CONTROLLER_BUTTON_B; + } + + for (x=0; x<_jlControllerCount; x++) { + if (SDL_GameControllerGetButton(_jlControllers[x], button)) { + pressed = true; + break; + } + } + + return pressed; +} + + +bool jlKeyPressed(void) { + jlUtilIdle(); + return (_jlNumKeysDown > 0); +} + + +char jlKeyRead(void) { + jlUtilIdle(); + return _jlLastKey; +} + + +void jlPaletteSet(byte index, byte r, byte g, byte b) { + _jlBackingStore->palette[index].r = r; + _jlBackingStore->palette[index].g = g; + _jlBackingStore->palette[index].b = b; +} + + +void jlSoundFree(jlSoundT *sound) { + if (sound != NULL) { + if (sound->data != NULL) { + Mix_FreeChunk((Mix_Chunk *)sound->data); + } + jlFree(sound); + } +} + + +bool jlSoundIsPlaying(jlSoundT *sound) { + return (Mix_Playing(sound->channel) > 0); +} + + +bool _jlSoundLoad(jlSoundT **sound, char *filename) { + bool result = false; + Mix_Chunk *sample; + sample = Mix_LoadWAV(filename); + if (sample) { + if (*sound != NULL) { + jlSoundFree(*sound); + } + jlSoundT *t = (jlSoundT *)jlMalloc(sizeof(jlSoundT)); + if (t != NULL) { + t->data = (void *)sample; + *sound = t; + result = true; + } + } + return result; +} + + +void jlSoundMusicContinue(void) { + Mix_ResumeMusic(); +} + + +bool jlSoundMusicIsPlaying(void) { + return (bool)Mix_PlayingMusic(); +} + + + +void jlSoundMusicPause(void) { + Mix_PauseMusic(); +} + + +void jlSoundMusicPlay(char *name) { + char temp[256]; + if (_jlMusicHandle != NULL) { + jlSoundMusicStop(); + } + snprintf(temp, 256, "%s.mod", name); + _jlMusicHandle = Mix_LoadMUS(temp); + if (_jlMusicHandle != NULL) { + Mix_PlayMusic(_jlMusicHandle, 1); + } else { + printf("Unable to load %s\n", temp); + } +} + + +void jlSoundMusicStop(void) { + if (jlSoundMusicIsPlaying()) { + Mix_HaltMusic(); + } + if (_jlMusicHandle != NULL) { + Mix_FreeMusic(_jlMusicHandle); + _jlMusicHandle = NULL; + } +} + + +void jlSoundPlay(jlSoundT *sound) { + sound->channel = Mix_PlayChannel(-1, (Mix_Chunk *)sound->data, 0); +} + + +bool _jlStaCreate(jlStaT **sta) { + jlStaT *t = (jlStaT *)jlMalloc(sizeof(jlStaT)); + if (t != NULL) { + memset(t, 0, sizeof(jlStaT)); + t->id[0] = 'S'; + t->id[1] = 'T'; + t->id[2] = 'A'; + t->version = 0; + // Backing store does not exist at startup + if (_jlBackingStore != NULL) { + memcpy(t->palette, _jlBackingStore->palette, sizeof(t->palette)); + memcpy(t->pixels, _jlBackingStore->pixels, sizeof(t->pixels)); + } + *sta = t; + return true; + } + return false; +} + + +void jlStaDisplay(jlStaT *sta) { + memcpy(_jlBackingStore->palette, sta->palette, sizeof(sta->palette)); + memcpy(_jlBackingStore->pixels, sta->pixels, sizeof(sta->pixels)); +} + + +char _jlUtilIdleCheckKey(int sym) { + + char key = 0; + + // Do we even care about this key? + if ((sym < 8) || (sym > 127)) { + key = 0; + } else { + SDL_Keymod mod = SDL_GetModState(); + key = (char)sym; + // Map LF to CR + if (key == 10) { + key = 13; + } + // Map DEL to BS + if (key == 127) { + key = 8; + } + // Convert to uppercase if needed + if ((mod == KMOD_SHIFT) && (key >= 'a') && (key <= 'z')) { + key -= 32; + } + // Is this outside the range we care about? + if ((key < ' ') || (key > '~')) { + if ((key != 8) && (key != 13) && (key != 27)) { + key = 0; + } + } + } + + return key; +} + + +void jlUtilIdle(void) { + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_KEYDOWN: + // Track keydown/keyup and remember last key pressed. + switch (event.key.keysym.sym) { + case SDLK_F1: + //SDL_RenderSetScale(_jlRenderer, 1.0f, 1.0f); + SDL_SetWindowSize(_jlWindow, 320, 200); + break; + + case SDLK_F2: + //SDL_RenderSetScale(_jlRenderer, 2.0f, 2.0f); + SDL_SetWindowSize(_jlWindow, 640, 400); + break; + + case SDLK_F3: + //SDL_RenderSetScale(_jlRenderer, 3.0f, 3.0f); + SDL_SetWindowSize(_jlWindow, 860, 600); + break; + + default: + _jlLastKey = _jlUtilIdleCheckKey(event.key.keysym.sym); + if (_jlLastKey > 0) { + _jlNumKeysDown++; + } + } + break; + + case SDL_KEYUP: + if (_jlUtilIdleCheckKey(event.key.keysym.sym) > 0) { + _jlNumKeysDown--; + } + break; + + case SDL_QUIT: + _jlIsRunning = false; + break; + } + } + + SDL_Delay(1); +} + + +bool jlUtilMustExit(void) { + return !_jlIsRunning; +} + + +void jlUtilShutdown(void) { + int x; + + SDL_RemoveTimer(_jlTimerId); + for (x=0; x<_jlControllerCount; x++) { + SDL_GameControllerClose(_jlControllers[x]); + } + _jlControllerCount = 0; + jlFree(_jlControllers); + jlStaFree(_jlBackingStore); + jlSoundMusicStop(); + Mix_CloseAudio(); + Mix_Quit(); + SDL_FreeFormat(_jlPixelFormat); + SDL_DestroyTexture(_jlTexture); + SDL_DestroyRenderer(_jlRenderer); + SDL_DestroyWindow(_jlWindow); + SDL_Quit(); + jlUtilDie("Clean Exit."); +} + + +void jlUtilStartup(char *appName) { + + int flags; + int result; + int joysticks; + int x; + + // Start low-level tools + if (SDL_Init(SDL_INIT_EVERYTHING) == -1) { + jlUtilDie(SDL_GetError()); + } + + // Start audio mixer & music system. + flags = MIX_INIT_MOD; + result = Mix_Init(flags); + if ((result & flags) != flags) { + jlUtilDie(Mix_GetError()); + } + if (Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 2, 4096 ) == -1) { + jlUtilDie(Mix_GetError()); + } + + // Set up controllers + joysticks = SDL_NumJoysticks(); + _jlControllers = (SDL_GameController **)jlMalloc(sizeof(SDL_GameController *) * (unsigned long)joysticks); + for (x=0; x + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. +*/ + + +#include +#include +#include +#include + + +#define JOEY_LIBRARY +#include "joey.h" + + +#include "stddclmr.h" + + +// Vector image file command bytes +#define COMMAND_COLOR 1 // C +#define COMMAND_CLEAR 2 // E +#define COMMAND_PLOT 3 // D (Draw) +#define COMMAND_LINE 4 // L +#define COMMAND_FILL 5 // F +#define COMMAND_PALETTE 6 // P +#define COMMAND_RESET 7 // R +#define COMMAND_HBOX 8 // B (Box) +#define COMMAND_FBOX 9 // S (Solid Box) +#define COMMAND_FILLTO 10 // T (FillTo) + +#define VEC_SCALE_DOWN 0.8 // 256/320 +#define VEC_SCALE_UP 1.25 // 320/256 + + +typedef struct { + int StartX; + int EndX; + int Y; + signed char Dir; // 'signed' needs to be specified for ORCA/C + bool ScanLeft; + bool ScanRight; + bool padding; // Aligns structure on x86 +} _jlScanDataT; + + +byte _jlDrawColor = 15; // Color in lower nibble only +byte _jlDrawColorNibbles = (15 << 4) + 15; // Color in both nibbles +byte _jlBorderColor = 0; + + +static jlColorT _jlDefaultPalette[16]; +static jlStackT *_jlFillStackTop = NULL; +static byte _jlDrawFillColor = 0; + + +void _jlDrawCircleClipped(int x0, int y0, int radius); +void _jlDrawFill(int x, int y, bool how); +void _jlDrawFillAddLine(int startX, int endX, int y, int ignoreStart, int ignoreEnd, char dir, bool isNextInDir, bool how); +_jlScanDataT *_jlDrawFillNewSegment(int startX, int endX, int y, signed char dir, bool scanLeft, bool scanRight); + + +#ifdef JOEY_DEBUG + +static jlMemoryBlockT _jlMemoryBlocks[JOEY_MEM_BLOCKS]; +static long _jlTotalAllocated = 0; +static long _jlTotalAllocations = 0; +static long _jlTotalFrees = 0; +static long _jlHighWaterMark = 0; +static long _jlBlocksNeeded = 0; +static bool _jlMemoryStarted = false; + +void _jlFree(void **pointer) { + int i; + + _jlTotalFrees++; + if (*pointer != NULL) { + for (i=0; i= JOEY_MEM_BLOCKS) jlUtilDie("Block not found in jlFree()!"); + + free(*pointer); + *pointer = NULL; + } +} + +void *_jlMalloc(size_t size, int line, char *file) { + int i; + + if (!_jlMemoryStarted) { + memset(_jlMemoryBlocks, 0, sizeof(_jlMemoryBlocks)); + _jlMemoryStarted = true; + } + + _jlTotalAllocations++; + for (i=0; i _jlBlocksNeeded) _jlBlocksNeeded = i + 1; + if (_jlMemoryBlocks[i].addr == NULL) jlUtilDie("Unable to allocate memory in jlMalloc()!"); + break; + } + } + if (i >= JOEY_MEM_BLOCKS) jlUtilDie("No free blocks for jlMalloc()!"); + + _jlTotalAllocated += size; + if (_jlTotalAllocated > _jlHighWaterMark) { + _jlHighWaterMark = _jlTotalAllocated; + } + + return _jlMemoryBlocks[i].addr; +} + +void *_jlRealloc(void *pointer, size_t size) { + int i = 0; + size_t diff = 0; + + if (pointer != NULL) { + for (i=0; i= JOEY_MEM_BLOCKS) jlUtilDie("No matching block for jlRealloc()!"); + + _jlTotalAllocated += diff; + if (_jlTotalAllocated > _jlHighWaterMark) { + _jlHighWaterMark = _jlTotalAllocated; + } + + return _jlMemoryBlocks[i].addr; +} + +#endif + + +void jlDisplayBorder(jlBorderColorsE color) { + _jlBorderColor = (byte)color; +} + + +void jlDrawBox(int x1, int y1, int x2, int y2) { + jlDrawLine(x1, y1, x2, y1); + jlDrawLine(x2, y1, x2, y2); + jlDrawLine(x2, y2, x1, y2); + jlDrawLine(x1, y2, x1, y1); +} + + +void jlDrawBoxFilled(int x1, int y1, int x2, int y2) { + int y; + for (y=y1; y<=y2; y++) { + jlDrawLine(x1, y, x2, y); + } +} + + +void _jlDrawCircleClipped(int x0, int y0, int radius) { + int x = radius-1; + int y = 0; + int dx = 1; + int dy = 1; + int err = dx - (radius << 1); + + while (x >= y) { + if ((x0 + x < 320) && (y0 + y < 200)) jlDrawPoint(x0 + x, y0 + y); + if ((x0 + y < 320) && (y0 + x < 200)) jlDrawPoint(x0 + y, y0 + x); + if ((x0 - y < 320) && (y0 + x < 200)) jlDrawPoint(x0 - y, y0 + x); + if ((x0 - x < 320) && (y0 + y < 200)) jlDrawPoint(x0 - x, y0 + y); + if ((x0 - x < 320) && (y0 - y < 200)) jlDrawPoint(x0 - x, y0 - y); + if ((x0 - x < 320) && (y0 - x < 200)) jlDrawPoint(x0 - y, y0 - x); + if ((x0 + y < 320) && (y0 - x < 200)) jlDrawPoint(x0 + y, y0 - x); + if ((x0 + x < 320) && (y0 - y < 200)) jlDrawPoint(x0 + x, y0 - y); + + if (err <= 0) { + y++; + err += dy; + dy += 2; + } + + if (err > 0) { + x--; + dx += 2; + err += dx - (radius << 1); + } + } +} + + +void jlDrawCircle(int x0, int y0, int radius) { + int x = radius-1; + int y = 0; + int dx = 1; + int dy = 1; + int err = dx - (radius << 1); + + // Is any of this going to be off the screen? + //***TODO*** All our drawing primitives should do this. + if ((x0 + radius > 319) || (x0 - radius < 0) || (y0 + radius > 199) || (y0 - radius < 0)) { + _jlDrawCircleClipped(x0, y0, radius); + return; + } + + while (x >= y) { + jlDrawPoint(x0 + x, y0 + y); + jlDrawPoint(x0 + y, y0 + x); + jlDrawPoint(x0 - y, y0 + x); + jlDrawPoint(x0 - x, y0 + y); + jlDrawPoint(x0 - x, y0 - y); + jlDrawPoint(x0 - y, y0 - x); + jlDrawPoint(x0 + y, y0 - x); + jlDrawPoint(x0 + x, y0 - y); + + if (err <= 0) { + y++; + err += dy; + dy += 2; + } + + if (err > 0) { + x--; + dx += 2; + err += dx - (radius << 1); + } + } +} + + +void jlDrawColor(byte index) { + _jlDrawColor = index; + _jlDrawColorNibbles = (byte)((index << 4) + index); +} + + +// http://members.chello.at/~easyfilter/bresenham.html +void jlDrawEllipse(int x0, int y0, int x1, int y1) { + int a = abs(x1-x0), b = abs(y1-y0), b1 = b&1; /* values of diameter */ + long dx = 4*(1-a)*b*b, dy = 4*(b1+1)*a*a; /* error increment */ + long err = dx+dy+b1*a*a, e2; /* error of 1.step */ + + if (x0 > x1) { x0 = x1; x1 += a; } /* if called with swapped points */ + if (y0 > y1) y0 = y1; /* .. exchange them */ + y0 += (b+1)/2; y1 = y0-b1; /* starting pixel */ + a *= 8*a; b1 = 8*b*b; + + do { + jlDrawPoint(x1, y0); /* I. Quadrant */ + jlDrawPoint(x0, y0); /* II. Quadrant */ + jlDrawPoint(x0, y1); /* III. Quadrant */ + jlDrawPoint(x1, y1); /* IV. Quadrant */ + e2 = 2*err; + if (e2 <= dy) { y0++; y1--; err += dy += a; } /* y step */ + if (e2 >= dx || 2*err > dy) { x0++; x1--; err += dx += b1; } /* x step */ + } while (x0 <= x1); + + while (y0-y1 < b) { /* too early stop of flat ellipses a=1 */ + jlDrawPoint(x0-1, y0); /* -> finish tip of ellipse */ + jlDrawPoint(x1+1, y0++); + jlDrawPoint(x0-1, y1); + jlDrawPoint(x1+1, y1--); + } +} + + +_jlScanDataT *_jlDrawFillNewSegment(int startX, int endX, int y, signed char dir, bool scanLeft, bool scanRight) { + _jlScanDataT *s = (_jlScanDataT *)jlMalloc(sizeof(_jlScanDataT)); + s->StartX = startX; + s->EndX = endX; + s->Y = y; + s->Dir = dir; + s->ScanLeft = scanLeft; + s->ScanRight = scanRight; + return s; +} + + +void _jlDrawFillAddLine(int startX, int endX, int y, int ignoreStart, int ignoreEnd, char dir, bool isNextInDir, bool how) { + int regionStart = -1; + int x; + + for (x=startX; x= ignoreEnd) && (jlDrawGetPixel(x, y) == _jlDrawFillColor)) { + jlDrawPoint(x, y); + if (regionStart < 0) regionStart = x; + } else if (regionStart >= 0) { + jlUtilStackPush(_jlFillStackTop, _jlDrawFillNewSegment(regionStart, x, y, dir, regionStart == startX, false)); + regionStart = -1; + } + } else { + // Unrolled conents to reduce comparison complexity. + if ((isNextInDir || x < ignoreStart || x >= ignoreEnd) && (jlDrawGetPixel(x, y) != _jlDrawFillColor)) { + jlDrawPoint(x, y); + if (regionStart < 0) regionStart = x; + } else if (regionStart >= 0) { + jlUtilStackPush(_jlFillStackTop, _jlDrawFillNewSegment(regionStart, x, y, dir, regionStart == startX, false)); + regionStart = -1; + } + } + if (!isNextInDir && x < ignoreEnd && x >= ignoreStart) x = ignoreEnd-1; + } + if (regionStart >= 0) { + jlUtilStackPush(_jlFillStackTop, _jlDrawFillNewSegment(regionStart, x, y, dir, regionStart == startX, true)); + } +} + + +// Stole this from http://www.adammil.net/blog/v126_A_More_Efficient_Flood_Fill.html +void _jlDrawFill(int x, int y, bool how) { + int height = 200; + int width = 320; + _jlScanDataT *r; + int startX; + int endX; + + // how == true; Fill on top of _jlDrawFillColor + // how == false; Fill on top of any color until we encounter _jlDrawFillColor + + jlDrawPoint(x, y); + + jlUtilStackPush(_jlFillStackTop, _jlDrawFillNewSegment(x, x+1, y, 0, true, true)); + while ((r = jlUtilStackPop(_jlFillStackTop)) != NULL) { + startX = r->StartX; + endX = r->EndX; + if (r->ScanLeft) { + if (how) { + while (startX > 0 && (jlDrawGetPixel(startX-1, r->Y) == _jlDrawFillColor)) jlDrawPoint(--startX, r->Y); + } else { + while (startX > 0 && (jlDrawGetPixel(startX-1, r->Y) != _jlDrawFillColor)) jlDrawPoint(--startX, r->Y); + } + } + if (r->ScanRight) { + if (how) { + while(endX < width && (jlDrawGetPixel(endX, r->Y) == _jlDrawFillColor)) jlDrawPoint(endX++, r->Y); + } else { + while(endX < width && (jlDrawGetPixel(endX, r->Y) == _jlDrawFillColor)) jlDrawPoint(endX++, r->Y); + } + } + r->StartX--; + r->EndX++; + if (r->Y > 0) _jlDrawFillAddLine(startX, endX, r->Y-1, r->StartX, r->EndX, -1, r->Dir <= 0, how); + if (r->Y < height-1) _jlDrawFillAddLine(startX, endX, r->Y+1, r->StartX, r->EndX, 1, r->Dir >= 0, how); + jlFree(r); + } +} + + +void jlDrawFill(int x, int y) { + _jlDrawFillColor = jlDrawGetPixel(x, y); + _jlDrawFill(x, y, true); +} + + +void jlDrawFillTo(int x, int y, byte color) { + _jlDrawFillColor = color; + _jlDrawFill(x, y, false); +} + + +void jlDrawLine(int x1, int y1, int x2, int y2) { + int x; + int y; + int dx; + int dy; + int incX; + int incY; + int balance; + + if (x2 >= x1) { + dx = x2 - x1; + incX = 1; + } else { + dx = x1 - x2; + incX = -1; + } + + if (y2 >= y1) { + dy = y2 - y1; + incY = 1; + } else { + dy = y1 - y2; + incY = -1; + } + + x = x1; + y = y1; + + if (dx >= dy) { + dy <<= 1; + balance = dy - dx; + dx <<= 1; + while (x != x2) { + jlDrawPoint(x, y); + if (balance >= 0) { + y += incY; + balance -= dx; + } + balance += dy; + x += incX; + } + jlDrawPoint(x, y); + } else { + dx <<= 1; + balance = dx - dy; + dy <<= 1; + while (y != y2) { + jlDrawPoint(x, y); + if (balance >= 0) { + x += incX; + balance -= dy; + } + balance += dx; + y += incY; + } + jlDrawPoint(x, y); + } +} + + +void jlKeyWaitForAny(void) { + while (!jlKeyPressed() && !jlUtilMustExit()) ; + jlKeyRead(); + while (jlKeyPressed() && !jlUtilMustExit()) ; +} + + +void jlPaletteDefault(void) { + byte i; + // Set palette. + _jlDefaultPalette[ 0].r = 0; _jlDefaultPalette[ 0].g = 0; _jlDefaultPalette[ 0].b = 0; // 000000 Black + _jlDefaultPalette[ 1].r = 0; _jlDefaultPalette[ 1].g = 0; _jlDefaultPalette[ 1].b = 10; // 0000AA Blue + _jlDefaultPalette[ 2].r = 0; _jlDefaultPalette[ 2].g = 10; _jlDefaultPalette[ 2].b = 0; // 00AA00 Green + _jlDefaultPalette[ 3].r = 0; _jlDefaultPalette[ 3].g = 10; _jlDefaultPalette[ 3].b = 10; // 00AAAA Cyan + _jlDefaultPalette[ 4].r = 10; _jlDefaultPalette[ 4].g = 0; _jlDefaultPalette[ 4].b = 0; // AA0000 Red + _jlDefaultPalette[ 5].r = 10; _jlDefaultPalette[ 5].g = 0; _jlDefaultPalette[ 5].b = 10; // AA00AA Magenta + _jlDefaultPalette[ 6].r = 10; _jlDefaultPalette[ 6].g = 5; _jlDefaultPalette[ 6].b = 0; // AA5500 Brown + _jlDefaultPalette[ 7].r = 10; _jlDefaultPalette[ 7].g = 10; _jlDefaultPalette[ 7].b = 10; // AAAAAA Light Gray + _jlDefaultPalette[ 8].r = 5; _jlDefaultPalette[ 8].g = 5; _jlDefaultPalette[ 8].b = 5; // 555555 Dark Gray + _jlDefaultPalette[ 9].r = 5; _jlDefaultPalette[ 9].g = 5; _jlDefaultPalette[ 9].b = 15; // 5555FF Bright Blue + _jlDefaultPalette[10].r = 5; _jlDefaultPalette[10].g = 15; _jlDefaultPalette[10].b = 5; // 55FF55 Bright Green + _jlDefaultPalette[11].r = 5; _jlDefaultPalette[11].g = 15; _jlDefaultPalette[11].b = 15; // 55FFFF Bright Cyan + _jlDefaultPalette[12].r = 15; _jlDefaultPalette[12].g = 5; _jlDefaultPalette[12].b = 5; // FF5555 Bright Red + _jlDefaultPalette[13].r = 15; _jlDefaultPalette[13].g = 5; _jlDefaultPalette[13].b = 15; // FF55FF Bright Magenta + _jlDefaultPalette[14].r = 15; _jlDefaultPalette[14].g = 15; _jlDefaultPalette[14].b = 5; // FFFF55 Bright Yellow + _jlDefaultPalette[15].r = 15; _jlDefaultPalette[15].g = 15; _jlDefaultPalette[15].b = 15; // FFFFFF White + for (i=0; i<16; i++) { + jlPaletteSet(i, _jlDefaultPalette[i].r, _jlDefaultPalette[i].g, _jlDefaultPalette[i].b); + } +} + + +bool _jlStaCopy(jlStaT *source, jlStaT **target) { + jlStaT *t = NULL; + // Are we copying into a new image? + if (*target == NULL) { + t = (jlStaT *)jlMalloc(sizeof(jlStaT)); + if (t == NULL) { + return false; + } + *target = t; + } + t = (jlStaT *)*target; + memcpy(*target, source, sizeof(jlStaT)); + return true; +} + + +void jlStaFree(jlStaT *sta) { + if (sta != NULL) { + jlFree(sta); + } +} + + +bool _jlStaLoad(jlStaT **sta, char *filename) { + bool result = false; + jlStaT *s = NULL; + FILE *f = NULL; + + // Are we loading into a new image? + if (*sta == NULL) { + s = (jlStaT *)jlMalloc(sizeof(jlStaT)); + if (s == NULL) { + return result; + } + *sta = s; + } + s = (jlStaT *)*sta; + // Load into it. + f = fopen(filename, "rb"); + if (f != NULL) { + if (fread(s, sizeof(jlStaT), 1, f) > 0) { + // Is this a valid image file? + if ((s->id[0] == 'S') && (s->id[1] == 'T') && (s->id[2] == 'A') && (s->version <= 0)) { + result = true; + } + } + fclose(f); + } + return result; +} + + +bool jlStaSave(jlStaT *sta, char *filename) { + bool result = false; + FILE *f; + + f = fopen(filename, "wb"); + if (f != NULL) { + if (fwrite(sta, sizeof(jlStaT), 1, f) > 0) { + result = true; + } + } + return result; +} + + +__attribute__((__format__ (__printf__, 1, 0))) +void jlUtilDie(const char *why, ...) { +#ifdef JOEY_DEBUG + int i; + FILE *f = NULL; + char msg[80]; // Very short messages (screen width). Be careful! + va_list va; + + va_start(va, why); + vsprintf(msg, why, va); + va_end(va); + + f = fopen("JLSTATS", "wt"); + if (f != NULL) { + fprintf(f, "JoeyLib Statistics\n\n"); + fprintf(f, " Allocations: %ld\n", _jlTotalAllocations); + fprintf(f, " Frees: %ld\n", _jlTotalFrees); + fprintf(f, " Most Used: %ldM (%ldK) (%ld bytes)\n", _jlHighWaterMark / 1048576, _jlHighWaterMark / 1024, _jlHighWaterMark); + fprintf(f, " Blocks Used: %ld (of %ld)\n", _jlBlocksNeeded, (long)JOEY_MEM_BLOCKS); + if (_jlTotalAllocations > _jlTotalFrees) { + for (i=0; idata; + *stack = s->previous; + jlFree(s); + } + return d; +} + + +void _jlUtilStackPush(jlStackT **stack, void *data) { + jlStackT *s = NULL; + s = (jlStackT *)jlMalloc(sizeof(jlStackT)); + s->previous = *stack; + s->data = data; + *stack = s; +} + + +void jlVecDisplay(jlVecT *vec, int ox, int oy) { + int command; + int count; + int i; + int x1; + int y1; + int x2; + int y2; + long p = 0; + + while (p < vec->length) { + command = vec->data[p++]; + switch (command) { + + case COMMAND_COLOR: + x1 = vec->data[p++]; + jlDrawColor((byte)x1); + break; + + case COMMAND_CLEAR: + jlDrawClear(); + break; + + case COMMAND_PLOT: + count = vec->data[p++]; + for (i=0; idata[p++] * VEC_SCALE_UP); + y1 = oy + vec->data[p++]; + jlDrawPoint(x1, y1); + } + break; + + case COMMAND_LINE: + count = vec->data[p++]; + x1 = ox + (int)(vec->data[p++] * VEC_SCALE_UP); + y1 = oy + vec->data[p++]; + for (i=2; idata[p++] * VEC_SCALE_UP); + y2 = oy + vec->data[p++]; + jlDrawLine(x1, y1, x2, y2); + x1 = x2; + y1 = y2; + } + break; + + case COMMAND_FILL: + count = vec->data[p++]; + for (i=0; idata[p++] * VEC_SCALE_UP); + y1 = oy + vec->data[p++]; + jlDrawFill(x1, y1); + } + break; + + case COMMAND_PALETTE: + count = vec->data[p++]; + for (i=0; idata[p++]; + y1 = vec->data[p++]; + x2 = vec->data[p++]; + y2 = vec->data[p++]; + jlPaletteSet((byte)x1,(byte)y1, (byte)x2, (byte)y2); + } + break; + + case COMMAND_RESET: + jlPaletteDefault(); + break; + + case COMMAND_HBOX: + count = vec->data[p++]; + for (i=0; idata[p++] * VEC_SCALE_UP); + y1 = oy + vec->data[p++]; + x2 = ox + (int)(vec->data[p++] * VEC_SCALE_UP); + y2 = oy + vec->data[p++]; + jlDrawBox(x1, y1, x2, y2); + } + break; + + case COMMAND_FBOX: + count = vec->data[p++]; + for (i=0; idata[p++] * VEC_SCALE_UP); + y1 = oy + vec->data[p++]; + x2 = ox + (int)(vec->data[p++] * VEC_SCALE_UP); + y2 = oy + vec->data[p++]; + jlDrawBoxFilled(x1, y1, x2, y2); + } + break; + + case COMMAND_FILLTO: + count = vec->data[p++]; + for (i=0; idata[p++] * VEC_SCALE_UP); + y1 = oy + vec->data[p++]; + x2 =vec->data[p++]; + jlDrawFillTo(x1, y1, (byte)x2); + } + break; + + + } // switch + } // while +} + + +void jlVecFree(jlVecT *vec) { + if (vec != NULL) { + jlFree(vec->data); + jlFree(vec); + } +} + + +bool _jlVecLoad(jlVecT **vec, char *filename) { + bool result = false; + jlVecT *v = NULL; + FILE *f = NULL; + long size; + + // Are we loading into a new image? + if (*vec != NULL) { + jlVecFree(*vec); + } + v = (jlVecT *)jlMalloc(sizeof(jlVecT)); + if (v == NULL) { + return result; + } + *vec = v; + v = (jlVecT *)*vec; + // Load into it. + f = fopen(filename, "rb"); + if (f != NULL) { + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + v->length = (unsigned int)((unsigned long)size - (sizeof(char) * 3) + (sizeof(byte))); + // Load header. + v->id[0] = (char)fgetc(f); + v->id[1] = (char)fgetc(f); + v->id[2] = (char)fgetc(f); + v->version = (byte)fgetc(f); + // Is this a valid image file? + if ((v->id[0] == 'V') && (v->id[1] == 'E') && (v->id[2] == 'C') && (v->version <= 0)) { + v->data = (byte *)jlMalloc(sizeof(byte) * v->length); + if (fread(v->data, sizeof(byte), v->length, f) > 0) { + result = true; + } else { + jlFree(v->data); + } + } + fclose(f); + } + return result; +} diff --git a/src/joey.h b/src/joey.h new file mode 100644 index 0000000..3db6cb1 --- /dev/null +++ b/src/joey.h @@ -0,0 +1,261 @@ +/* + * JoeyLib + * Copyright (C) 2018 Scott Duensing + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef H_JOEY_ +#define H_JOEY_ + +#define JOEY_DEBUG + + +#include + + +#ifndef bool +#define bool unsigned char +#define true 1 +#define false 0 +#endif + +#define byte unsigned char + + +extern byte _jlDrawColor; +extern byte _jlDrawColorNibbles; +extern byte _jlBorderColor; + + +enum _jlBorderColorsE { + BORDER_BLACK = 0, + BORDER_DEEP_RED = 1, + BORDER_DEEP_BLUE = 2, + BORDER_PURPLE = 3, + BORDER_DARK_GREEN = 4, + BORDER_DARK_GRAY = 5, + BORDER_MEDIUM_BLUE = 6, + BORDER_LIGHT_BLUE = 7, + BORDER_BROWN = 8, + BORDER_ORANGE = 9, + BORDER_LIGHT_GRAY = 10, + BORDER_PINK = 11, + BORDER_GREEN = 12, + BORDER_YELLOW = 13, + BORDER_AQUAMARINE = 14, + BORDER_WHITE = 15 +}; +typedef enum _jlBorderColorsE jlBorderColorsE; + + +typedef struct { + byte b : 4; + byte g : 4; + byte r : 4; + byte reserved : 4; +} jlColorT; + +typedef struct { + byte l : 4; + byte r : 4; +} jlPixelPairT; + +typedef struct { + void *data; + int channel; +} jlSoundT; + +typedef struct { + char id[3]; + byte version; + jlColorT palette[16]; // 4 bits reserved, 4 bits red, 4 green, 4 blue + jlPixelPairT pixels[32000]; // 320x200, 4 bits per pixel +} jlStaT; + +typedef struct _jlStackS { + struct _jlStackS *previous; + void *data; +} jlStackT; + +typedef struct { + char id[3]; + byte version; + unsigned int length; + byte *data; +} jlVecT; + + +// Memory Management +#ifdef JOEY_DEBUG + +#define JOEY_MEM_BLOCKS 64 + +typedef struct { + void *addr; + char *file; + size_t size; + int line; +} jlMemoryBlockT; + +#define jlFree(p) _jlFree((void **)&(p)) +void _jlFree(void **pointer); + +#define jlMalloc(s) _jlMalloc(s, __LINE__, __FILE__) +void *_jlMalloc(size_t size, int line, char *file); + +#define jlRealloc(p, s) _jlRealloc(p, s) +void *_jlRealloc(void *pointer, size_t size); + +#else + +#define jlFree free +#define jlMalloc malloc + +#endif + + +// Determine platform and settings +#ifdef __linux__ + +#define JOEY_LINUX +#define JOEY_PC +#define JOEY_LITLE_ENDIAN +#define segment(x) + +#elif _WIN32 + +#define JOEY_WINDOWS +#define JOEY_PC +#define JOEY_LITLE_ENDIAN +#define segment(x) + +#elif __APPLE__ + +#define JOEY_MACOS +#define JOEY_PC +#define JOEY_LITLE_ENDIAN +#define segment(x) + +#elif __ORCAC__ + +#define JOEY_IIGS +#define JOEY_LITLE_ENDIAN +#define __attribute__(x) +#ifdef JOEY_LIBRARY +#pragma noroot +segment "joeylib"; +#endif +#pragma memorymodel 1 +#pragma optimize -1 +#pragma lint -1 +#pragma debug 0 + +#elif AMIGA + +#define JOEY_AMIGA +#define JOEY_BIG_ENDIAN +#define __attribute__(x) +#define segment(x) + +#else + +#define JOEY_ST +#define JOEY_BIG_ENDIAN +#define __attribute__(x) +#define segment(x) + +#endif + + +void jlDisplayBorder(jlBorderColorsE color); +void jlDisplayPresent(void); +void jlDrawBlit8x8(jlStaT *sta, int cx1, int cy1, int cx2, int cy2); +void jlDrawBox(int x1, int y1, int x2, int y2); +void jlDrawBoxFilled(int x1, int y1, int x2, int y2); +void jlDrawCircle(int x, int y, int radius); +void jlDrawClear(void); +void jlDrawColor(byte index); +void jlDrawEllipse(int x1, int y1, int x2, int y2); +void jlDrawFill(int x, int y); +void jlDrawFillTo(int x, int y, byte color); +byte jlDrawGetPixel(int x, int y); +void jlDrawLine(int x1, int y1, int x2, int y2); +void jlDrawPoint(int x, int y); +int jlGameGetAxis(byte which); +bool jlGameGetButton(byte which); +bool jlKeyPressed(void); +char jlKeyRead(void); +void jlKeyWaitForAny(void); +void jlPaletteDefault(void); +void jlPaletteSet(byte index, byte r, byte g, byte b); +void jlSoundFree(jlSoundT *sound); +bool jlSoundIsPlaying(jlSoundT *sound); +#define jlSoundLoad(sound, filename) _jlSoundLoad((jlSoundT **)&(sound), filename) // Syntatic Sugar +bool _jlSoundLoad(jlSoundT **sound, char *filename); +void jlSoundMusicContinue(void); +bool jlSoundMusicIsPlaying(void); +void jlSoundMusicPause(void); +void jlSoundMusicPlay(char *name); +void jlSoundMusicStop(void); +void jlSoundPlay(jlSoundT *sound); +#define jlStaCopy(source, target) _jlStaCopy(source, (jlStaT **)&(target)) // Syntatic Sugar +bool _jlStaCopy(jlStaT *source, jlStaT **target); +#define jlStaCreate(sta) _jlStaCreate((jlStaT **)&(sta)) // Syntatic Sugar +bool _jlStaCreate(jlStaT **sta); +void jlStaDisplay(jlStaT *sta); +void jlStaFree(jlStaT *sta); +#define jlStaLoad(sta, filename) _jlStaLoad((jlStaT **)&(sta), filename) // Syntatic Sugar +bool _jlStaLoad(jlStaT **sta, char *filename); +bool jlStaSave(jlStaT *sta, char *filename); +void jlUtilDie(const char *why, ...) __attribute__((noreturn)); +void jlUtilIdle(void); +#define jlUtilIsOdd(x) (((x & 1) == 1) ? true:false) +bool jlUtilMustExit(void); +void jlUtilShutdown(void) __attribute__((noreturn)); +#define jlUtilStackPop(stack) _jlUtilStackPop((jlStackT **)&(stack)) // Syntatic Sugar +void *_jlUtilStackPop(jlStackT **stack); +#define jlUtilStackPush(stack, data) _jlUtilStackPush((jlStackT **)&(stack), data) // Syntatic Sugar +void _jlUtilStackPush(jlStackT **stack, void *data); +void jlUtilStartup(char *appName); +unsigned int jlUtilTimer(void); +void jlVecDisplay(jlVecT *vec, int x, int y); +void jlVecFree(jlVecT *vec); +#define jlVecLoad(vec, filename) _jlVecLoad((jlVecT **)&(vec), filename) // Syntatic Sugar +bool _jlVecLoad(jlVecT **vec, char *filename); + + +#ifdef JOEY_IIGS +// Inlined functions - asm code +extern void asmB88(byte *p, int cx1, int cy1, int cx2, int cy2); +extern void asmPoint(int color, int x, int y); +extern int asmGetPoint(int x, int y); +extern unsigned int asmGetVbl(void); + +// Inlined functions +#define jlDrawBlit8x8(sta, cx1, cy1, cx2, cy2) asmB88((byte *)sta->pixels, cx1, cy1, cx2, cy2); +#define jlDrawGetPixel(x, y) asmGetPoint(x, y) +#define jlDrawPoint(x, y) asmPoint(_jlDrawColorNibbles, x, y) +#define jlUtilTimer asmGetVbl + +#ifdef JOEY_DEBUG +void _jlDebugBorder(jlBorderColorsE color); +#endif +#endif // JOEY_IIGS + + +#endif // H_JOEY_ diff --git a/src/kanga.sta b/src/kanga.sta new file mode 100644 index 0000000000000000000000000000000000000000..cef14ce980e438917920a3c14d084788b22ebacf GIT binary patch literal 32036 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 TGZ<;D^)H_@^h&1xauMYJfUm`> literal 0 HcmV?d00001 diff --git a/src/music.mod b/src/music.mod new file mode 100644 index 0000000000000000000000000000000000000000..fbbc9ee3b86c9f08a2ab5cd92c13cfe72ca6e89a GIT binary patch literal 43826 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 G>i-{M+hBkI literal 0 HcmV?d00001 diff --git a/src/stddclmr.h b/src/stddclmr.h new file mode 100644 index 0000000..8e6b3d9 --- /dev/null +++ b/src/stddclmr.h @@ -0,0 +1,95 @@ +#ifndef STDDCLMR_H +#define STDDCLMR_H + +/* +Action figures sold separately. Add toner. All models over 18 years of age. +All rights reserved. Allow four to six weeks for delivery. An equal +opportunity employer. Any resemblance to actual persons, living or dead, is +unintentional and purely coincidental. Apply only to affected area. Approved +for veterans. As seen on TV. At participating locations only. Avoid contact +with mucous membranes. Avoid contact with skin. Avoid extreme temperatures +and store in a cool dry place. Batteries not included. Be sure each item is +properly endorsed. Beware of dog. Booths for two or more. Breaking seal +constitutes acceptance of agreement. Call toll free number before digging. +Caveat emptor. Check here if tax deductible. Close cover before striking +Colors may fade. Contains a substantial amount of non-tobacco ingredients. +Contents may settle during shipment. Contestants have been briefed on some +questions before the show. Copyright 1995 Joker's Wild. Disclaimer does +not cover hurricane, lightning, tornado, tsunami, volcanic eruption, +earthquake, flood, and other Acts of God, misuse, neglect, unauthorized +repair, damage from improper installation, broken antenna or marred cabinet, +incorrect line voltage, missing or altered serial numbers, sonic boom +vibrations, electromagnetic radiation from nuclear blasts, customer +adjustments that are not covered in the joke list, and incidents owing to +airplane crash, ship sinking, motor vehicle accidents, leaky roof, broken +glass, falling rocks, mud slides, forest fire, flying projectiles, or +dropping the item. Do not bend, fold, mutilate, or spindle. Do not place +near flammable or magnetic source. Do not puncture, incinerate, or store +above 120 degrees Fahrenheit. Do not stamp. Use other side for additional +listings. Do not use while operating a motor vehicle or heavy equipment. Do +not write below this line. Documents are provided "as is" without any +warranties expressed or implied. Don't quote me on anything. Don't quote me +on that. Driver does not carry cash. Drop in any mailbox. Edited for +television. Employees and their families are not eligible. Falling rock. +First pull up, then pull down. Flames redirected to /dev/null. For a +limited time only. For external use only. For off-road use only. For office +use only. For recreational use only. Do not disturb. Freshest if eaten +before date on carton. Hand wash only, tumble dry on low heat. If a rash, +redness, irritation, or swelling develops, discontinue use. If condition +persists, consult your physician. If defects are discovered, do not attempt +to fix them yourself, but return to an authorized service center. If +ingested, do not induce vomiting, if symptoms persist, consult a doctor. +Keep away from open flames and avoid inhaling fumes. Keep away from +sunlight, pets, and small children. Keep cool; process promptly. Limit +one-per-family please. Limited time offer, call now to ensure prompt +delivery. List at least two alternate dates. List each check separately by +bank number. List was current at time of printing. Lost ticket pays maximum +rate. May be too intense for some viewers. Must be 18 to enter. No Canadian +coins. No alcohol, dogs or horses. No anchovies unless otherwise specified. +No animals were harmed in the production of these documents. No money down. +No other warranty expressed or implied. No passes accepted for this +engagement. No postage necessary if mailed in the United States. No +preservatives added. No purchase necessary. No salt, MSG, artificial color +or flavor added. No shoes, no shirt, no service, no kidding. No solicitors. +No substitutions allowed. No transfers issued until the bus comes to a +complete stop. No user-serviceable parts inside. Not affiliated with the +American Red Cross. Not liable for damages due to use or misuse. Not +recommended for children. Not responsible for direct, indirect, incidental +or consequential damages resulting from any defect, error or failure to +perform. Not the Beatles. Objects in mirror may be closer than they appear. +One size fits all. Many suitcases look alike. Other copyright laws for +specific entries apply wherever noted. Other restrictions may apply. Package +sold by weight, not volume. Parental advisory - explicit lyrics. Penalty for +private use. Place stamp here. Please remain seated until the ride has come +to a complete stop. Possible penalties for early withdrawal. Post office will +not deliver without postage. Postage will be paid by addressee. Prerecorded +for this time zone. Price does not include taxes. Processed at location +stamped in code at top of carton. Quantities are limited while supplies last. +Read at your own risk. Record additional transactions on back of previous +stub. Replace with same type. Reproduction strictly prohibited. Restaurant +package, not for resale. Return to sender, no forwarding order on file, +unable to forward. Safety goggles may be required during use. Sanitized for +your protection. Sealed for your protection, do not use if the safety seal is +broken. See label for sequence. Shading within a garment may occur. Sign here +without admitting guilt. Simulated picture. Slightly enlarged to show detail. +Slightly higher west of the Rockies. Slippery when wet. Smoking these may be +hazardous to your health. Some assembly required. Some equipment shown is +optional. Some of the trademarks mentioned in this product appear for +identification purposes only. Subject to FCC approval. Subject to change +without notice. Substantial penalty for early withdrawal. Text may contain +material some readers may find objectionable, parental guidance is advised. +Text used in these documents is made from 100% recycled electrons and magnetic +particles. These documents do not reflect the thoughts or opinions of either +myself, my company, my friends, or my rabbit. This is not an offer to sell +securities. This offer is void where prohibited, taxed, or otherwise +restricted. This product is meant for educational purposes only. Times +approximate. Unix is a registered trademark of AT&T. Use only as directed. Use +only in a well-ventilated are. User assumes full liabilities. Void where +prohibited. We have sent the forms which seem right for you. You must be +present to win. You need not be present to win. Your canceled check is your +receipt. Your mileage may vary. I didn't do it. You can't prove anything. + +This supersedes all previous notices. +*/ + +#endif // STDDCLMR_H diff --git a/src/test.c b/src/test.c new file mode 100644 index 0000000..fab60ab --- /dev/null +++ b/src/test.c @@ -0,0 +1,87 @@ +/* + * JoeyLib + * Copyright (C) 2018 Scott Duensing + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. +*/ + + +#include +#include +#include + + +#include "joey.h" + + +#ifdef JOEY_IIGS +segment "testapp"; +#endif + + +// Font hacking! +__attribute__((__format__ (__printf__, 4, 0))) +void printAt(jlStaT *font, int cx, int cy, const char *what, ...) { + int x; + int y; + int counter; + char msg[40]; // Very short messages (screen width). Be careful! + va_list va; + + va_start(va, what); + vsprintf(msg, what, va); + va_end(va); + + for (counter=0; counter<(int)strlen(msg); counter++) { + y = (msg[counter] - ' ') / 40; + x = (msg[counter] - ' ') % 40; + jlDrawBlit8x8(font, x, y, counter + cx, cy); + } +} + + +int main(void) { + + jlStaT *kanga = NULL; + jlStaT *font = NULL; + + jlUtilStartup("JoeyLib Test"); + + jlStaLoad(kanga, "kanga.sta"); + jlStaLoad(font, "font.sta"); + + jlStaDisplay(kanga); + jlDrawColor(1); + jlDrawBox(0, 0, 319, 199); + + jlSoundMusicPlay("music"); + + jlPaletteSet(15, 15, 15, 15); + while (!jlKeyPressed()) { + printAt(font, 1, 20, "T = %d ", jlUtilTimer()); + jlDisplayPresent(); + } + jlKeyRead(); + + jlSoundMusicStop(); + + jlStaFree(font); + jlStaFree(kanga); + + jlUtilShutdown(); + +}