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

View File

@ -27,6 +27,7 @@ import jace.config.InvokableAction;
import jace.core.Computer; import jace.core.Computer;
import jace.core.RAMEvent; import jace.core.RAMEvent;
import jace.core.RAMListener; import jace.core.RAMListener;
import jace.core.Video;
import javafx.scene.image.PixelWriter; import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage; import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
@ -331,6 +332,19 @@ public class VideoNTSC extends VideoDHGR {
{1.0, 0.0, 0.0}, //1111 f {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() { private void initDivideTables() {
for (int i = 0; i < 560; i++) { for (int i = 0; i < 560; i++) {
divBy28[i] = i / 28; divBy28[i] = i / 28;
@ -361,10 +375,18 @@ public class VideoNTSC extends VideoDHGR {
} }
static public int yiqToRgb(double y, double i, double q) { 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 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 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); 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) { public static double normalize(double x, double minX, double maxX) {

View File

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

View File

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

View File

@ -426,7 +426,7 @@ public class CardAppleMouse extends Card {
* Described in Apple Mouse technical note #7 * Described in Apple Mouse technical note #7
* Cn1A: Read mouse clamping values * Cn1A: Read mouse clamping values
* Register number is stored in $478 and ranges from x47 to x4e * 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: * Values should be returned in this order:
* MinXH, MinYH, MinXL, MinYL, MaxXH, MaxYH, MaxXL, MaxYL * 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 * 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 { public class ZipWarpAccelerator extends Device {
@ConfigurableField(category = "debug", name = "Debug messages") @ConfigurableField(category = "debug", name = "Debug messages")
public boolean debugMessagesEnabled = false; public boolean debugMessagesEnabled = false;

View File

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

View File

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