Setting up new repository with just the JoeyLib code in it.

This commit is contained in:
Scott Duensing 2018-10-14 20:50:14 -05:00
commit 21b2306e9b
16 changed files with 3097 additions and 0 deletions

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
*.sta filter=lfs diff=lfs merge=lfs -text
*.mod filter=lfs diff=lfs merge=lfs -text

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
*~
*.user
lib/
src/SDL2/
src/music
src/music.w
src/*.dis

50
build-IIgs.sh Executable file
View file

@ -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

69
build-PC.sh Executable file
View file

@ -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

45
joeylib.pro Normal file
View file

@ -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

BIN
src/font.sta Normal file

Binary file not shown.

721
src/jIIgs.asm Normal file
View file

@ -0,0 +1,721 @@
;----------------------------------------
; JoeyLib
; Copyright (C) 2018 Scott Duensing <scott@kangaroopunch.com>
;
; 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

332
src/jIIgs.c Normal file
View file

@ -0,0 +1,332 @@
/*
* JoeyLib
* Copyright (C) 2018 Scott Duensing <scott@kangaroopunch.com>
*
* 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 <string.h>
#include <locator.h>
#include <memory.h>
#include <misctool.h>
#include <sound.h>
#undef false
#undef true
#define JOEY_LIBRARY
#include "joey.h"
#ifdef JOEY_DEBUG
#include <stdio.h>
#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();
}

119
src/jIIgs.macro Normal file
View file

@ -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

546
src/jPC.c Normal file
View file

@ -0,0 +1,546 @@
/*
* JoeyLib
* Copyright (C) 2018 Scott Duensing <scott@kangaroopunch.com>
*
* 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 <stdio.h>
#include <string.h>
#include <limits.h>
#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<joysticks; x++) {
if (SDL_IsGameController(x)) {
_jlControllers[_jlControllerCount++] = SDL_GameControllerOpen(x);
}
}
// Create a window and renderer using SDL
_jlWindow = SDL_CreateWindow(appName, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 400, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
_jlRenderer = SDL_CreateRenderer(_jlWindow, -1, SDL_RENDERER_SOFTWARE);
_jlTexture = SDL_CreateTexture(_jlRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 320, 200);
_jlPixelFormat = SDL_AllocFormat(SDL_GetWindowPixelFormat(_jlWindow));
// Create backing store
jlStaCreate(_jlBackingStore);
jlPaletteDefault();
jlDrawColor(0);
jlDrawClear();
jlDrawColor(15);
jlDisplayPresent();
// Start 1/10th second timer
_jlTimerId = SDL_AddTimer(100, _jlUtilTimer, NULL);
}
Uint32 _jlUtilTimer(Uint32 interval, void *param) {
(void)param;
_jlTimerValue++;
return(interval);
}
unsigned int jlUtilTimer(void) {
return _jlTimerValue;
}

763
src/joey.c Normal file
View file

@ -0,0 +1,763 @@
/*
* JoeyLib
* Copyright (C) 2018 Scott Duensing <scott@kangaroopunch.com>
*
* 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 <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#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; i++) {
if (_jlMemoryBlocks[i].addr == *pointer) {
_jlTotalAllocated -= _jlMemoryBlocks[i].size;
_jlMemoryBlocks[i].addr = 0;
_jlMemoryBlocks[i].size = 0;
break;
}
}
if (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<JOEY_MEM_BLOCKS; i++) {
if (_jlMemoryBlocks[i].addr == 0) {
_jlMemoryBlocks[i].addr = malloc(size);
_jlMemoryBlocks[i].size = size;
_jlMemoryBlocks[i].line = line;
_jlMemoryBlocks[i].file = file;
if (i + 1 > _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; i++) {
if (_jlMemoryBlocks[i].addr == pointer) {
diff = size - _jlMemoryBlocks[i].size;
_jlMemoryBlocks[i].size = size;
_jlMemoryBlocks[i].addr = realloc(_jlMemoryBlocks[i].addr, size);
if (_jlMemoryBlocks[i].addr == NULL) jlUtilDie("Unable to allocate memory in jlRealloc()!");
break;
}
}
}
if (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<endX; x++) {
if (how) {
// 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;
}
} 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; i<JOEY_MEM_BLOCKS; i++) {
if (_jlMemoryBlocks[i].addr != NULL) {
fprintf(f, "Unfreed Block: %ld bytes, %d @ %s\n", (long int)_jlMemoryBlocks[i].size, _jlMemoryBlocks[i].line, _jlMemoryBlocks[i].file);
}
}
}
//fprintf(f, "jlBorderSaved %d %d\n", jlBorderSaved.rtcReserved, jlBorderSaved.border);
fprintf(f, "\n%s\n", msg);
fclose(f);
}
#else
(void)why;
#endif
exit(0);
}
void *_jlUtilStackPop(jlStackT **stack) {
void *d = NULL;
jlStackT *s;
if (*stack != NULL) {
s = *stack;
d = s->data;
*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; i<count; i += 2) {
x1 = ox + (int)(vec->data[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; i<count; i += 2) {
x2 = ox + (int)(vec->data[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; i<count; i += 2) {
x1 = ox + (int)(vec->data[p++] * VEC_SCALE_UP);
y1 = oy + vec->data[p++];
jlDrawFill(x1, y1);
}
break;
case COMMAND_PALETTE:
count = vec->data[p++];
for (i=0; i<count; i += 4) {
x1 = vec->data[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; i<count; i += 4) {
x1 = ox + (int)(vec->data[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; i<count; i += 4) {
x1 = ox + (int)(vec->data[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; i<count; i += 2) {
x1 = ox + (int)(vec->data[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;
}

261
src/joey.h Normal file
View file

@ -0,0 +1,261 @@
/*
* JoeyLib
* Copyright (C) 2018 Scott Duensing <scott@kangaroopunch.com>
*
* 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 <stdlib.h>
#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_

BIN
src/kanga.sta Normal file

Binary file not shown.

BIN
src/music.mod Normal file

Binary file not shown.

95
src/stddclmr.h Normal file
View file

@ -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

87
src/test.c Normal file
View file

@ -0,0 +1,87 @@
/*
* JoeyLib
* Copyright (C) 2018 Scott Duensing <scott@kangaroopunch.com>
*
* 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 <stdio.h>
#include <string.h>
#include <stdarg.h>
#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();
}