commit 21b2306e9b3e8c0fad27031d6a192703a1f074d1 Author: Scott Duensing Date: Sun Oct 14 20:50:14 2018 -0500 Setting up new repository with just the JoeyLib code in it. 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 0000000..f488f74 Binary files /dev/null and b/src/font.sta differ 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 0000000..cef14ce Binary files /dev/null and b/src/kanga.sta differ diff --git a/src/music.mod b/src/music.mod new file mode 100644 index 0000000..fbbc9ee Binary files /dev/null and b/src/music.mod differ 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(); + +}