Compare commits

...

6 Commits

Author SHA1 Message Date
Peter Ferrie 723769a66a
Merge d4d3c59b8c into 24fd496d31 2024-04-23 10:08:43 -05:00
Brendan Robert 24fd496d31 Tweaks to timing/speaker sound, visual sound debug feature added 2024-04-23 10:08:38 -05:00
Peter Ferrie d4d3c59b8c defer case check to be faster 2023-09-03 15:08:16 -07:00
Peter Ferrie c540527370 another fix 2023-09-03 14:42:51 -07:00
Peter Ferrie 3f359cf3ff fix a couple of bugs, compare a bit faster 2023-09-03 14:41:10 -07:00
Peter Ferrie b258ed441a decompress faster 2023-09-02 11:30:15 -07:00
9 changed files with 100 additions and 67 deletions

View File

@ -52,6 +52,9 @@ public class Speaker extends Device {
@ConfigurableField(category = "sound", name = "1mhz timing", description = "Force speaker output to 1mhz?")
public static boolean force1mhz = true;
@ConfigurableField(category = "sound", name = "Show sound", description = "Use black color value to show sound output")
public static boolean showSound = false;
@InvokableAction(category = "sound", name = "Record sound", description="Toggles recording (saving) sound output to a file", defaultKeyMapping = "ctrl+shift+w")
public static void toggleFileOutput() {
if (fileOutputActive) {
@ -95,20 +98,23 @@ public class Speaker extends Device {
*/
@ConfigurableField(name = "Speaker Volume", shortName = "vol", description = "Should be under 1400")
public static int VOLUME = 400;
/**
* Number of idle cycles until speaker playback is deactivated
*/
@ConfigurableField(name = "Idle cycles before sleep", shortName = "idle")
public static int MAX_IDLE_CYCLES = 2000000;
private int currentVolume = 0;
private int fadeOffAmount = 1;
/**
* Manifestation of the apple speaker softswitch
*/
private boolean speakerBit = false;
private double TICKS_PER_SAMPLE = ((double) TimedDevice.NTSC_1MHZ) / SoundMixer.RATE;
private double TICKS_PER_SAMPLE_FLOOR = Math.floor(TICKS_PER_SAMPLE);
private static double TICKS_PER_SAMPLE = ((double) TimedDevice.NTSC_1MHZ) / SoundMixer.RATE;
private RAMListener listener = null;
private SoundBuffer buffer = null;
/**
* Number of idle cycles until speaker playback is deactivated
*/
@ConfigurableField(name = "Idle cycles before sleep", shortName = "idle")
// public static int MAX_IDLE_CYCLES = (int) (SoundMixer.BUFFER_SIZE * TICKS_PER_SAMPLE * 2);
public static int MAX_IDLE_CYCLES = (int) TimedDevice.NTSC_1MHZ / 4;
/**
* Suspend playback of sound
*
@ -118,7 +124,6 @@ public class Speaker extends Device {
public boolean suspend() {
boolean result = super.suspend();
speakerBit = false;
Emulator.withComputer(c->c.getMotherboard().cancelSpeedRequest(this));
if (buffer != null) {
try {
buffer.shutdown();
@ -128,6 +133,7 @@ public class Speaker extends Device {
buffer = null;
}
}
Emulator.withComputer(c->c.getMotherboard().cancelSpeedRequest(this));
return result;
}
@ -164,7 +170,6 @@ public class Speaker extends Device {
} else {
TICKS_PER_SAMPLE = Emulator.withComputer(c-> ((double) c.getMotherboard().getSpeedInHz()) / SoundMixer.RATE, 0.0);
}
TICKS_PER_SAMPLE_FLOOR = Math.floor(TICKS_PER_SAMPLE);
super.resume();
}
@ -172,9 +177,9 @@ public class Speaker extends Device {
* Reset idle counter whenever sound playback occurs
*/
public void resetIdle() {
currentVolume = VOLUME;
idleCycles = 0;
if (!isRunning()) {
speakerBit = false;
resume();
}
}
@ -186,21 +191,32 @@ public class Speaker extends Device {
*/
@Override
public void tick() {
if (idleCycles++ >= MAX_IDLE_CYCLES) {
suspend();
}
if (speakerBit) {
level++;
if (showSound) {
VideoNTSC.CHANGE_BLACK_COLOR(40, 20, 20);
}
} else if (showSound) {
VideoNTSC.CHANGE_BLACK_COLOR(20,20,40);
}
if (idleCycles++ >= MAX_IDLE_CYCLES && (currentVolume <= 0 || !speakerBit)) {
suspend();
if (showSound) {
VideoNTSC.CHANGE_BLACK_COLOR(0,0,0);
}
}
counter += 1.0d;
if (counter >= TICKS_PER_SAMPLE) {
playSample(level * VOLUME);
Emulator.withComputer(c->c.getMotherboard().requestSpeed(this));
if (idleCycles >= MAX_IDLE_CYCLES) {
currentVolume -= fadeOffAmount;
}
playSample(level * currentVolume);
// Emulator.withComputer(c->c.getMotherboard().requestSpeed(this));
// Set level back to 0
level = 0;
// Set counter to 0
counter -= TICKS_PER_SAMPLE_FLOOR;
counter -= TICKS_PER_SAMPLE;
}
}

View File

@ -640,7 +640,7 @@ public class VideoDHGR extends Video {
Logger.getLogger(getClass().getName()).warning("Went out of bounds in video display");
}
}
static final Color BLACK = Color.BLACK;
static Color BLACK = Color.BLACK;
static Color WHITE = Color.WHITE;
static final int[][] XY_OFFSET;

View File

@ -27,6 +27,7 @@ import jace.config.InvokableAction;
import jace.core.Computer;
import jace.core.RAMEvent;
import jace.core.RAMListener;
import jace.core.Video;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
@ -331,6 +332,19 @@ public class VideoNTSC extends VideoDHGR {
{1.0, 0.0, 0.0}, //1111 f
};
public static void CHANGE_BLACK_COLOR(int r, int g, int b) {
Emulator.withVideo(v->{
VideoNTSC vntsc = (VideoNTSC) v;
BLACK = Color.rgb(r, g, b);
int c = colorToInt(BLACK);
for (int i1 = 0; i1 < 4; i1++) {
vntsc.SOLID_PALETTE[i1][0] = c;
vntsc.TEXT_PALETTE[i1][0] = c;
}
});
Video.forceRefresh();
}
private void initDivideTables() {
for (int i = 0; i < 560; i++) {
divBy28[i] = i / 28;
@ -361,10 +375,18 @@ public class VideoNTSC extends VideoDHGR {
}
static public int yiqToRgb(double y, double i, double q) {
return colorToInt(yiqToRgbColor(y, i, q));
}
static public Color yiqToRgbColor(double y, double i, double q) {
int r = (int) (normalize((y + 0.956 * i + 0.621 * q), 0, 1) * 255);
int g = (int) (normalize((y - 0.272 * i - 0.647 * q), 0, 1) * 255);
int b = (int) (normalize((y - 1.105 * i + 1.702 * q), 0, 1) * 255);
return (255 << 24) | (r << 16) | (g << 8) | b;
return Color.rgb(r, g, b);
}
static public int colorToInt(Color c) {
return (int) (255 << 24) | (int) (c.getRed() * 255) << 16 | (int) (c.getGreen() * 255) << 8 | (int) (c.getBlue() * 255);
}
public static double normalize(double x, double minX, double maxX) {

View File

@ -188,6 +188,8 @@ public abstract class TimedDevice extends Device {
return null;
}
cycleTimer = 0;
long retVal = nextSync;
nextSync = Math.max(nextSync, System.nanoTime()) + nanosPerInterval;
if (isMaxSpeed() || useParentTiming()) {
if (tempSpeedDuration > 0) {
tempSpeedDuration -= cyclesPerInterval;
@ -195,11 +197,8 @@ public abstract class TimedDevice extends Device {
disableTempMaxSpeed();
}
}
nextSync += nanosPerInterval;
return null;
}
long retVal = nextSync;
nextSync = Math.min(nextSync + nanosPerInterval, System.nanoTime() + nanosPerInterval * 2); // Avoid drift (but not too much!
return retVal;
}
}

View File

@ -274,7 +274,7 @@ public abstract class Video extends TimedDevice {
Emulator.withVideo(v->v._forceRefresh());
}
private void _forceRefresh() {
protected void _forceRefresh() {
lineDirty = true;
screenDirty = true;
forceRedrawRowCount = APPLE_SCREEN_LINES + 1;

View File

@ -426,7 +426,7 @@ public class CardAppleMouse extends Card {
* Described in Apple Mouse technical note #7
* Cn1A: Read mouse clamping values
* Register number is stored in $478 and ranges from x47 to x4e
* Return value should be stored in $5782
* Return value should be stored in $578
* Values should be returned in this order:
* MinXH, MinYH, MinXL, MinYL, MaxXH, MaxYH, MaxXL, MaxYL
*/

View File

@ -24,6 +24,7 @@ import jace.core.RAMListener;
/**
* Implements a basic hardware accelerator that is able to adjust the speed of the emulator
*/
// TODO: Support the registers used here: https://github.com/a2-4am/4cade/blob/main/src/hw.accel.a#L238
public class ZipWarpAccelerator extends Device {
@ConfigurableField(category = "debug", name = "Debug messages")
public boolean debugMessagesEnabled = false;

View File

@ -33,8 +33,9 @@ DEBUG = 0
decomp !zone {
jsr .chkdst
ldy #0 ; In lit loop Y must be zero
sec
.fill1A jsr .getbt2
jmp .fill1B
bne .fill1B ; always taken
.incdst inc pDst+1
.chkdst ldx pDst+1
@ -174,19 +175,19 @@ decomp !zone {
bne .gshift ; always taken
; Get another 8 bits into our bit buffer. Destroys X. Preserves A. Requires Y=0.
; Carry is always set on entry, Z always clear on exit
; Alternately, use .getbt2 to preserve X and destroy A
.getbts tax
.getbt2 lda (pSrc),y
inc pSrc
beq .src3A
.src3B sec
rol
sta bits
txa
inc pSrc
beq .src3A
rts
.src3A inc pSrc+1
bne .src3B ; always taken
rts
} ; end of zone

View File

@ -364,7 +364,7 @@ export asm memcpy(pSrc, pDst, len, auxWr)#0
inc pTmp+1
bne .pglup ; always taken
.part:
cpx #0
txa
beq .done
- lda (tmp),y
sta (pTmp),y
@ -390,8 +390,8 @@ export asm memset(pDst, val, len)#0
lda evalStkL,x ; len lo
pha
lda evalStkH,x ; len hi
tax
beq +
tax
lda tmp
- sta (pTmp),y
iny
@ -420,10 +420,10 @@ export asm readAuxByte(ptr)#1
sta $10-1,y
dey
bne -
jmp $10
.rdauxb
sei ; prevent interrupts while in aux mem
sta setAuxRd
jmp $10
.rdauxb
lda (pTmp),y
sta clrAuxRd
cli
@ -462,19 +462,19 @@ end
export asm finishString(isPlural)#1
!zone {
+asmPlasmRet 1
ldy prevCSWL+ABS_OFFSET ; put the cout vector back to default
sty cswl
ldy prevCSWL+1+ABS_OFFSET ; put the cout vector back to default
sty cswh
ldy #0 ; dest offset in Y (will be incremented before store)
cpy inbuf
beq .done1 ; failsafe: handle zero-length string
tax ; test for isPlural == 0
beq +
lda #$40 ; for setting V later
+ sta tmp ; save isPlural flag
lda prevCSWL+ABS_OFFSET ; put the cout vector back to default
sta cswl
lda prevCSWL+1+ABS_OFFSET ; put the cout vector back to default
sta cswh
clv ; V flag for prev-is-alpha
ldy #0 ; dest offset in Y (will be incremented before store)
ldx #0 ; source offset in X (will be incremented before load)
cpx inbuf
beq .done ; failsafe: handle zero-length string
.fetch
inx
lda inbuf,x ; get next input char
@ -488,11 +488,11 @@ export asm finishString(isPlural)#1
dey ; undo copy of the paren
stx tmp+1 ; save position in input
dex ; needed for failsafe operation
bit tmp ; set copy flag (V) initially to same as isPlural flag
.findsl ; see if there's a slash within the parens
inx
cpx inbuf
bcs .done ; failsafe: handle missing end-paren
lda inbuf,x
ora #$80 ; normalize hi-bit for comparisons below
cmp #"/"
@ -503,11 +503,8 @@ export asm finishString(isPlural)#1
pha
plp
+ cmp #")" ; scan until ending paren
beq +
cpx inbuf
bcc .findsl ; loop to scan next char
bcs .done ; failsafe: handle missing end-paren (always taken)
+ ldx tmp+1 ; get back to start of parens
bne .findsl ; loop to scan next char
ldx tmp+1 ; get back to start of parens
; copy mode flag is now in V: if slash present, single=copy, plural=nocopy
; if no slash: single=nocopy, plural=copy
.plup
@ -541,6 +538,7 @@ export asm finishString(isPlural)#1
.done
sty inbuf ; save new length
.done1
lda #<inbuf ; return pointer to string
ldy #>inbuf
rts
@ -575,8 +573,8 @@ export asm blit(isAux, srcData, dstScreenPtr, nLines, lineSize)#0
lsr ; to carry bit
bcc +
ldy #15 ; put aux copy routine in zero page
- lda .cpaux + ABS_OFFSET,y
sta $10,y
- ldx .cpaux + ABS_OFFSET,y
stx $10,y
dey
bpl -
+ pla ; get line count
@ -694,8 +692,9 @@ export asm puts(str)#0
sta pTmp
lda #'!'
ldx #1
sty pTmp+1
tya
beq + ; safety: print '!' instead of null string
sty pTmp+1
ldy #0
lda (pTmp),y
tax
@ -913,9 +912,8 @@ export asm rawDisplayStr(pStr)#0
lda (pTmp),y
sta tmp
- cpy tmp
bcc +
rts
+ iny
bcs ++
iny
lda (pTmp),y
ora #$80
cmp #"^"
@ -929,7 +927,8 @@ export asm rawDisplayStr(pStr)#0
+ sty tmp+1
jsr DisplayChar
ldy tmp+1
bne -
bne - ; always taken
++rts
end
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -991,8 +990,8 @@ export asm streqi(a, b)#1
lda evalStkL+1,x
sta pTmp
lda evalStkH+1,x
sta pTmp+1
beq .null
sta pTmp+1
ldy #0
lda (tmp),y
cmp (pTmp),y
@ -1004,21 +1003,16 @@ export asm streqi(a, b)#1
+ tax ; count up to (verified same) length of the strings
- iny
lda (tmp),y
cmp #('z'&$7F)+1 ; convert to upper case
bcs +
eor (pTmp),y
beq + ; matched
cmp #$20 ; check for case bit
bne .noteqi ; abort on alpha inequality
ora (tmp),y ; convert to lower case
cmp #('z'&$7F)+1
bcs .noteqi ; abort on inequality
cmp #'a'&$7F
bcc +
sbc #$20
+ sta ysav
lda (pTmp),y
cmp #('z'&$7F)+1 ; convert to upper case
bcs +
cmp #'a'&$7F
bcc +
sbc #$20
+ cmp ysav
bne .noteqi ; abort on inequality
dex
bcc .noteqi ; abort on inequality
+ dex
bne -
lda #1
ldy #0 ; okay, they're equal. Return 1 (not just any char; so that PLASMA when...is can work)