mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-24 05:25:12 +00:00
Merge branch 'master' of https://github.com/tildearrow/furnace into nmk112
This commit is contained in:
commit
f6849dfa50
19 changed files with 274 additions and 157 deletions
3
TODO.md
3
TODO.md
|
@ -4,11 +4,8 @@
|
||||||
|
|
||||||
# THE REAL TO-DO LIST
|
# THE REAL TO-DO LIST
|
||||||
|
|
||||||
- Amiga's Period Modulation not working
|
|
||||||
- Song is silent after playing an order after loop point
|
- Song is silent after playing an order after loop point
|
||||||
- Select loaded instrument on open - rewrite because I want a setting...
|
- Select loaded instrument on open - rewrite because I want a setting...
|
||||||
- re-engineer volume handling? Sound Unit cries at me
|
|
||||||
- finish status view
|
|
||||||
- finish auto-clone
|
- finish auto-clone
|
||||||
|
|
||||||
once you have done all of this (maybe not the first one) and merged the two or so pending pull requests, release 0.6.1
|
once you have done all of this (maybe not the first one) and merged the two or so pending pull requests, release 0.6.1
|
||||||
|
|
|
@ -21,7 +21,8 @@ the following keyboard shortcuts work while on a text field:
|
||||||
|
|
||||||
these work similar to text fields, but you may only input numbers.
|
these work similar to text fields, but you may only input numbers.
|
||||||
|
|
||||||
they also usually have `+` and `-` buttons which allow you to increase/decrease the amount when clicked (and rapidly do so when click-holding).
|
they also usually have `+` and `-` buttons which allow you to increase/decrease the value when clicked (and rapidly do so when holding).
|
||||||
|
additionally, Ctrl-clicking these buttons may increase/decrease the value by a coarse amount.
|
||||||
|
|
||||||
## sliders
|
## sliders
|
||||||
|
|
||||||
|
@ -29,7 +30,8 @@ sliders are used for controlling values in a quick manner by being dragged.
|
||||||
|
|
||||||
using the scroll wheel while holding Ctrl will change the slider's value by small amounts.
|
using the scroll wheel while holding Ctrl will change the slider's value by small amounts.
|
||||||
|
|
||||||
right-clicking or Ctrl-clicking or a slider (Command-click on macOS) will turn it into a number input field for a short period of time, allowing you to input precise values.
|
right-clicking or Ctrl-clicking or a slider (Command-click on macOS) will turn it into a number input field, allowing you to input precise values.
|
||||||
|
once you click away it will become a slider again.
|
||||||
|
|
||||||
## windows
|
## windows
|
||||||
|
|
||||||
|
@ -37,13 +39,13 @@ right-clicking or Ctrl-clicking or a slider (Command-click on macOS) will turn i
|
||||||
|
|
||||||
windows may be moved, collapsed, closed or even docked around the workspace.
|
windows may be moved, collapsed, closed or even docked around the workspace.
|
||||||
|
|
||||||
to move a window, press and hold the mouse button while on title bar or any empty space on it.
|
to move a window, press and hold the left mouse button while on the title bar or any empty space on it.
|
||||||
then drag your mouse, and release it to stop moving.
|
then drag your mouse, and release it to stop moving.
|
||||||
|
|
||||||
to resize a window, drag the bottom right corner (marked by a triangular tab) or the borders.
|
to resize a window, drag the bottom right corner (marked by a triangular tab) or the borders.
|
||||||
|
|
||||||
to collapse a window, click on the triangle in the title bar.
|
to collapse a window, click on the triangle in the title bar.
|
||||||
clicking again expands it.
|
clicking again expands the window.
|
||||||
|
|
||||||
to close a window, click on the `X` at the top right corner, or select it from the "window" menu.
|
to close a window, click on the `X` at the top right corner, or select it from the "window" menu.
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,28 @@ in this very computer music trackers were born...
|
||||||
|
|
||||||
imported MOD files use this chip, and will set A-4 tuning to 436.
|
imported MOD files use this chip, and will set A-4 tuning to 436.
|
||||||
|
|
||||||
|
## amplitude/period modulation
|
||||||
|
|
||||||
|
Amiga has support for (rather primitive) amplitude and period (frequency) modulation.
|
||||||
|
however, nobody has used this feature as it is rather useless, not well-documented and works in a complicated way.
|
||||||
|
|
||||||
|
Amiga sample playback is done by two chips: Paula (the one that you probably know) and Agnus (the one that actually feeds Paula with samples).
|
||||||
|
Agnus has several DMA (direct memory access) units which read from chip memory independent of the CPU. four of these DMA units are used for samples.
|
||||||
|
|
||||||
|
when DMA is enabled, Paula requests sample data from Agnus, and then plays these samples back.
|
||||||
|
there's a catch though. since the data bus is 16-bit, Paula requests **two** 8-bit samples at once! this explains why:
|
||||||
|
- the sample length registers are in words rather than bytes (thereby allowing samples up to 131070 in length)
|
||||||
|
- the maximum playback rate (31250Hz PAL; ~31469Hz NTSC) is two times the HBlank rate (Agnus fetches samples on HBlank, around 15625Hz on PAL or ~15734Hz on NTSC)
|
||||||
|
|
||||||
|
during normal sample playback, the first sample is output and then the second. afterwards, two more samples are fetched, and so on.
|
||||||
|
|
||||||
|
now, when amplitude or period modulation are enabled, things work differently.
|
||||||
|
the channel is silenced, and the two 8-bit samples are **treated as a big-endian 16-bit number**, which is then written to the next channel's volume or period.
|
||||||
|
|
||||||
|
in the case of amplitude modulation, only the second sample is significant because the volume register uses 7 bits (to represent 0 to 64 (65 to 127 are treated as 64)) and the other bits are ignored.
|
||||||
|
|
||||||
|
in the case of period modulation, both samples are significant. the first sample is the upper byte, and the second is the lower byte.
|
||||||
|
|
||||||
## effects
|
## effects
|
||||||
|
|
||||||
- `10xx`: **toggle low-pass filter.** `0` turns it off and `1` turns it on.
|
- `10xx`: **toggle low-pass filter.** `0` turns it off and `1` turns it on.
|
||||||
|
|
|
@ -7,6 +7,13 @@ this chip features:
|
||||||
- stereo soft panning
|
- stereo soft panning
|
||||||
- accepts either 8-bit PCM or proprietary 8-bit µ-law compressed PCM samples
|
- accepts either 8-bit PCM or proprietary 8-bit µ-law compressed PCM samples
|
||||||
|
|
||||||
|
## sample memory notice
|
||||||
|
|
||||||
|
this chip is rather unique when it comes to sample memory. be sure to read this notice.
|
||||||
|
|
||||||
|
the channels are in groups of four. a sample bank (128KB) may be selected for each group.
|
||||||
|
if a sample that is on a different bank plays in a group, the group is switched to that bank, and other channels will be silenced.
|
||||||
|
|
||||||
## effects
|
## effects
|
||||||
|
|
||||||
- `11xx`: **set noise mode.**
|
- `11xx`: **set noise mode.**
|
||||||
|
|
|
@ -18,6 +18,7 @@ as listed in the "Window" menu:
|
||||||
|
|
||||||
- [piano](piano.md)
|
- [piano](piano.md)
|
||||||
- [oscilloscope](osc.md)
|
- [oscilloscope](osc.md)
|
||||||
|
- [oscilloscope (X-Y)](xyosc.md)
|
||||||
- [oscilloscopes (per-channel)](chanosc.md)
|
- [oscilloscopes (per-channel)](chanosc.md)
|
||||||
- [clock](clock.md)
|
- [clock](clock.md)
|
||||||
- [register view](regview.md)
|
- [register view](regview.md)
|
||||||
|
|
|
@ -5,8 +5,8 @@ the "Channels" dialog allows manipulation of the song's channels.
|
||||||
![channels dialog](channels.png)
|
![channels dialog](channels.png)
|
||||||
|
|
||||||
each channel has the following options:
|
each channel has the following options:
|
||||||
- **Visible**: uncheck the box to hide the channel from view. pattern data will be kept.
|
- **Visible**: uncheck the box to hide the channel from the pattern view. pattern data will be kept.
|
||||||
- crossed-arrows button: click and drag to rearrange pattern data throughout the song.
|
- crossed-arrows button: click and drag to rearrange pattern data throughout the song.
|
||||||
- note: this does _not_ move channels around within a chip! it only affects pattern data.
|
- note: this does **not** move channels around! it only moves the channel's pattern data.
|
||||||
- **Name** is the name displayed at the top of each channel in the tracker view.
|
- **Name**: the name displayed at the top of each channel in the pattern view.
|
||||||
- to the right of that is the abbreviation used above each channel in the order view.
|
- the next setting is "short name", which is displayed in the orders view and/or when a channel is collapsed.
|
||||||
|
|
|
@ -1,25 +1,24 @@
|
||||||
# oscilloscope (per-channel)
|
# oscilloscope (per-channel)
|
||||||
|
|
||||||
the "Oscilloscope (per-channel)" dialog shows an individual oscilloscope for each channel during playback.
|
the "Oscilloscope (per-channel)" windows displays several oscilloscope views (one per channel).
|
||||||
|
|
||||||
![oscilloscope per-channel configuration view](chanosc.png)
|
![oscilloscope per-channel configuration view](chanosc.png)
|
||||||
|
|
||||||
right-clicking within the view will change it to the configuration view shown above:
|
right-clicking the view will display the configuration view shown above:
|
||||||
- **Columns**: arranges oscilloscopes into this many columns.
|
- **Columns**: sets the amount of columns for the oscilloscope views.
|
||||||
- **Size (ms)**: sets what length of audio is visible in each oscilloscope.
|
- **Size (ms)**: sets how much of a channel's output is captured for the oscilloscope view.
|
||||||
- **Center waveform**: does its best to latch the waveform to the channel's note frequency and centers the display.
|
- **Center waveform**: when enabled, the displayed waveforms will be centered using an auto-correlation algorithm.
|
||||||
- **Automatic columns**: sets the number of columns based on the number of channels.
|
- **Automatic columns**: sets the number of columns based on the number of channels.
|
||||||
- **Off**: use the Columns setting.
|
- **Off**: use the Columns setting.
|
||||||
- **Mode 1**: always fewer columns than rows.
|
- **Mode 1**: always fewer columns than rows.
|
||||||
- **Mode 2**: bias slightly toward more columns.
|
- **Mode 2**: bias slightly toward more columns.
|
||||||
- **Mode 3**: always more columns than rows.
|
- **Mode 3**: always more columns than rows.
|
||||||
- **Amplitude**: scales amplitude for all oscilloscopes.
|
- **Amplitude**: scales amplitude for all oscilloscope views.
|
||||||
- **Gradient**: see below.
|
- **Gradient**: this allows you to use a gradient for determining the waveforms' colors instead of a single color. see the gradient section for more information.
|
||||||
- the color selector sets the color for all waveforms. right-clicking on it pops up an option dialog:
|
- if this option is off, a color selector will be displayed. right-click on it for some options:
|
||||||
- select between the square selector and the color wheel selector.
|
- select between the square selector and the color wheel selector.
|
||||||
- **Alpha bar**: adds a transparency selector.
|
- **Alpha bar**: display an opacity bar.
|
||||||
- the boxes below that are for selecting colors numerically by red-green-blue-alpha, hue-saturation-value-alpha, and HTML-style RGBA in hex.
|
- **Text format**: this allows you to display some text on each oscilloscope view. the following codes may be used:
|
||||||
- **Text format**: this string determins what text is shown in the top-left of each oscilloscope. it can be any text, and the following shortcodes will be replaced with information about the channel:
|
|
||||||
- `%c`: channel name
|
- `%c`: channel name
|
||||||
- `%C`: channel short name
|
- `%C`: channel short name
|
||||||
- `%d`: channel number (starting from 0)
|
- `%d`: channel number (starting from 0)
|
||||||
|
@ -35,27 +34,32 @@ right-clicking within the view will change it to the configuration view shown ab
|
||||||
- `%V`: volume (percentage)
|
- `%V`: volume (percentage)
|
||||||
- `%b`: volume (hex)
|
- `%b`: volume (hex)
|
||||||
- `%%`: percent sign
|
- `%%`: percent sign
|
||||||
- the OK button returns from options view to the oscilloscopes.
|
|
||||||
|
click on OK to return to the main view.
|
||||||
|
|
||||||
## gradient
|
## gradient
|
||||||
|
|
||||||
![oscilloscope per-channel gradient configuration view](chanosc-gradient.png)
|
![oscilloscope per-channel gradient configuration view](chanosc-gradient.png)
|
||||||
|
|
||||||
in this mode, the color selector is replaced by a square field onto which circular "stops" can be placed. each stop adds a soft circle of color. the resulting image is used to look up the oscilloscope color as determined by each axis.
|
when enabling the Gradient setting, a gradient view is displayed in where circular "points" can be placed.
|
||||||
|
each point adds a color spot.
|
||||||
|
the resulting image is used to look up the waveform's color as determined by each axis.
|
||||||
|
|
||||||
- right-click to place a stop.
|
- right-click to place a point.
|
||||||
- left-click on a stop to change its color. the color selector is the same as above, with two additions:
|
- left-click on a point to change its color:
|
||||||
|
- a color picker is displayed, alongside two settings.
|
||||||
- **Distance**: the size of the circle.
|
- **Distance**: the size of the circle.
|
||||||
- **Spread**: the size of the solid center of the circle. increasing it fills more of the circle with the target color.
|
- **Spread**: the size of the solid center of the circle. increasing it fills more of the circle with the color.
|
||||||
|
- middle-click on a point to delete it.
|
||||||
|
|
||||||
- **Background**: sets background color for entire field.
|
- **Background**: sets the gradient's background color.
|
||||||
- **X Axis**: determines what the horizontal maps to, from left to right.
|
- **X Axis**: determines what the horizontal axis maps to.
|
||||||
- **Y Axis**: determines what the vertical maps to. from bottom to top. these can be set to the following:
|
- **Y Axis**: determines what the vertical axis maps to. these can be set to the following:
|
||||||
- **None (0%)**: stays at the left or bottom.
|
- **None (0%)**: always left or bottom
|
||||||
- **None (50%)**: stays at the center.
|
- **None (50%)**: always center
|
||||||
- **None (100%)**: stays at the right or top.
|
- **None (100%)**: always right or top
|
||||||
- **Frequency**: changes color with note frequency.
|
- **Frequency**: changes color with frequency
|
||||||
- **Volume**: changes color with volume.
|
- **Volume**: changes color with volume
|
||||||
- **Channel**: changes color based on channel number.
|
- **Channel**: changes color based on channel number (first channel is 0% and last is 100%)
|
||||||
- **Brightness**: currently does nothing.
|
- **Brightness**: currently does nothing
|
||||||
- **Note Trigger**: changes color when a new note is played.
|
- **Note Trigger**: changes color when a new note is played
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
# chip manager
|
# chip manager
|
||||||
|
|
||||||
the **chip manager** window does exactly what it says.
|
the **chip manager** window allows you to manage chips, including adding, changing, moving or removing them.
|
||||||
|
|
||||||
![chip manager](chip-manager.png)
|
![chip manager](chip-manager.png)
|
||||||
|
|
||||||
**Preserve channel order**: make existing pattern data stay in place even when chips are rearranged. if turned off, pattern data will rearrange to match (the default, and usually the desired behavior).
|
**Preserve channel order**: make existing pattern data stay in place even when chips are rearranged. when turned off, pattern data will be arranged to match (the default, and usually desired behavior).
|
||||||
|
|
||||||
to move a chip around, click and drag the ![crossed-arrows](chip-manager-move.png) button to its left.
|
to move a chip around, click and drag the ![crossed-arrows](chip-manager-move.png) button to the left.
|
||||||
|
|
||||||
to replace a chip with a different one, click the **Change** button and select the replacement.
|
to replace a chip with a different one, click the **Change** button. this will display the chip selector.
|
||||||
|
|
||||||
to remove a chip entirely, click the ![X](chip-manager-remove.png) button.
|
to remove a chip, click the ![X](chip-manager-remove.png) button.
|
||||||
|
|
||||||
click a chip's name to open its options, where one can set clock rate, chip variant, and other specifics.
|
click on a chip's name to open chip configuration. this allows you to change chip options, such as clock rate, chip type and so on.
|
||||||
|
|
|
@ -4,4 +4,4 @@
|
||||||
|
|
||||||
comments, credits, or any arbitrary text may be entered here. it has no effect on the song.
|
comments, credits, or any arbitrary text may be entered here. it has no effect on the song.
|
||||||
|
|
||||||
there is no word wrap; long lines must be broken manually with the Enter key.
|
there is no word wrap; long lines must be broken manually with the Enter/Return key.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# compatibility flags
|
# compatibility flags
|
||||||
|
|
||||||
the **Compatibility Flags** window contains several tabs full of settings that change aspects of tracking and playback. a new Furnace song will have these disabled, while opening a DefleMask module, .mod, or earlier Furnace file will automatically set the appropriate options.
|
the **Compatibility Flags** window contains several tabs full of settings that change playback behavior. a new Furnace song will have these disabled, while opening a DefleMask module, .mod, or earlier Furnace file will automatically set the appropriate options.
|
||||||
|
|
||||||
hovering over most options will bring up additional info about them. it is not recommended to change any of these, especially the ones in the DefleMask and Old Furnace sections.
|
hovering over most options will bring up additional info about them. it is not recommended to change any of these, especially the ones in the DefleMask and Old Furnace sections.
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
# grooves
|
# grooves
|
||||||
|
|
||||||
grooves are macros for speed.
|
a **groove** is the equivalent of repeating `0Fxx` effects on each row to get a cycle of speeds. for example, a groove of "6 4 5 3" makes the first row 6 ticks long, the next row 4 ticks, then 5, 3, 6, 4, 5, 3...
|
||||||
|
|
||||||
a **groove** is the equivalent of repeating `0Fxx` commands on each row to get a cycle of speeds. for example, a groove of "6 4 5 3" makes the first row 6 ticks long, the next row 4 ticks, then 5, 3, 6, 4, 5, 3...
|
|
||||||
|
|
||||||
|
|
||||||
![groove](groove.png)
|
![groove](groove.png)
|
||||||
|
|
||||||
|
@ -13,18 +10,17 @@ to set the song's groove:
|
||||||
- click again so it becomes "Groove".
|
- click again so it becomes "Groove".
|
||||||
- enter a sequence of up to 16 speeds.
|
- enter a sequence of up to 16 speeds.
|
||||||
|
|
||||||
|
|
||||||
![groove patterns](grooves.png)
|
![groove patterns](grooves.png)
|
||||||
|
|
||||||
the "Grooves" window is for entering preset groove patterns.
|
the "Grooves" window displays the list of groove patterns in the song.
|
||||||
- the **`+`** button adds a new groove pattern; click in the pattern to edit it.
|
- the **`+`** button adds a new groove pattern; click in the groove pattern to edit it.
|
||||||
- the **`×`** buttons remove them.
|
- the **`×`** buttons remove them.
|
||||||
|
|
||||||
a single `09xx` command will switch to the matching numbered groove pattern.
|
a single `09xx` command will switch to the matching numbered groove pattern.
|
||||||
|
|
||||||
## BPM
|
## tempo
|
||||||
|
|
||||||
this is a non-exhaustive list of grooves and their equivalent BPM.
|
this is a non-exhaustive list of grooves and their equivalent tempo in BPM.
|
||||||
|
|
||||||
note: this table assumes a song's tick rate setting is left at its default value for the chosen engine speed: 60 for NTSC, or 50 for PAL.
|
note: this table assumes a song's tick rate setting is left at its default value for the chosen engine speed: 60 for NTSC, or 50 for PAL.
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
# oscilloscope
|
# oscilloscope
|
||||||
|
|
||||||
the Oscilloscope shows the waveform of the mix of all currently playing sounds.
|
the Oscilloscope shows the audio output as a waveform.
|
||||||
|
|
||||||
![oscilloscope view](osc.png)
|
![oscilloscope view](osc.png)
|
||||||
|
|
||||||
right-clicking on the oscilloscope toggles the adjustment sliders:
|
right-clicking on the oscilloscope toggles adjustment sliders:
|
||||||
- waveform height zoom
|
- waveform height (zoom)
|
||||||
- width of viewed audio (window size) in milliseconds.
|
- window size (how much of the output to display) in milliseconds.
|
||||||
|
|
19
doc/8-advanced/xyosc.md
Normal file
19
doc/8-advanced/xyosc.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# oscilloscope (X-Y)
|
||||||
|
|
||||||
|
also called "vectorscope", this is similar to the normal oscilloscope in that it draws audio output as a waveform, but instead of being one-dimensional and going from left to right, it is plotted in two dimensions (usually X represents left output and Y represents right output).
|
||||||
|
|
||||||
|
(TODO: image)
|
||||||
|
|
||||||
|
right-clicking the X-Y oscilloscope window displays settings:
|
||||||
|
- **X Channel**: the output channel which will affect the X (horizontal) axis. default is 1 (left).
|
||||||
|
- **Invert**: flips the axis, therefore flipping the view horizontally.
|
||||||
|
- **Y Channel**: the output channel which will affect the Y (vertical) axis. default is 2 (right).
|
||||||
|
- **Invert**: flips the axis, therefore flipping the view vertically.
|
||||||
|
- **Zoom**: changes amplitude, making the plot bigger or smaller.
|
||||||
|
- **Samples**: the maximum number of samples to use for plotting.
|
||||||
|
- this setting may be replaced by another or removed in a future version.
|
||||||
|
- **Decay Time (ms)**: sets how long it takes for the points/lines to fade away.
|
||||||
|
- **Intensity**: changes the brightness of the dots/lines.
|
||||||
|
- **Line Thickness**: sets how thick lines are.
|
||||||
|
|
||||||
|
click **OK** to return to the oscilloscope view.
|
30
src/divasm/divasm.h
Normal file
30
src/divasm/divasm.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
struct DivASMResult {
|
||||||
|
int line, err;
|
||||||
|
DivASMResult():
|
||||||
|
line(-1),
|
||||||
|
err(0) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DivASMFile {
|
||||||
|
String name;
|
||||||
|
SafeReader* data;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum DivASMTarget {
|
||||||
|
DIV_ASM_TARGET_DUMMY=0,
|
||||||
|
DIV_ASM_TARGET_6502,
|
||||||
|
DIV_ASM_TARGET_SPC700
|
||||||
|
};
|
||||||
|
|
||||||
|
class DivASM {
|
||||||
|
std::vector<DivASMFile> files;
|
||||||
|
SafeWriter* result;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DivASMResult getError();
|
||||||
|
SafeWriter* assemble(String name);
|
||||||
|
void addFile(String name, SafeReader* data);
|
||||||
|
|
||||||
|
DivASM(DivASMTarget target);
|
||||||
|
~DivASM();
|
||||||
|
};
|
|
@ -149,7 +149,7 @@ std::vector<DivROMExportOutput> DivExportAmigaValidation::go(DivEngine* e) {
|
||||||
} else if (waveNum<65536) {
|
} else if (waveNum<65536) {
|
||||||
seq->writeC((i<<4)|4);
|
seq->writeC((i<<4)|4);
|
||||||
seq->writeS_BE(waveNum);
|
seq->writeS_BE(waveNum);
|
||||||
} else{
|
} else {
|
||||||
seq->writeC((i<<4)|1);
|
seq->writeC((i<<4)|1);
|
||||||
seq->writeC(waves[waveNum].pos>>16);
|
seq->writeC(waves[waveNum].pos>>16);
|
||||||
seq->writeC(waves[waveNum].pos>>8);
|
seq->writeC(waves[waveNum].pos>>8);
|
||||||
|
|
|
@ -81,7 +81,18 @@ const char** DivPlatformAmiga::getRegisterSheet() {
|
||||||
|
|
||||||
void DivPlatformAmiga::acquire(short** buf, size_t len) {
|
void DivPlatformAmiga::acquire(short** buf, size_t len) {
|
||||||
thread_local int outL, outR, output;
|
thread_local int outL, outR, output;
|
||||||
|
|
||||||
for (size_t h=0; h<len; h++) {
|
for (size_t h=0; h<len; h++) {
|
||||||
|
if (--delay<0) delay=0;
|
||||||
|
if (!writes.empty() && delay<=0) {
|
||||||
|
QueuedWrite w=writes.front();
|
||||||
|
|
||||||
|
if (w.addr==0x96 && !(w.val&0x8000)) delay=4096/AMIGA_DIVIDER;
|
||||||
|
|
||||||
|
amiga.write(w.addr,w.val);
|
||||||
|
writes.pop();
|
||||||
|
}
|
||||||
|
|
||||||
bool hsync=bypassLimits;
|
bool hsync=bypassLimits;
|
||||||
outL=0;
|
outL=0;
|
||||||
outR=0;
|
outR=0;
|
||||||
|
@ -99,7 +110,8 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) {
|
||||||
}
|
}
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
// run DMA
|
// run DMA
|
||||||
if (amiga.dmaEn && amiga.audEn[i] && !amiga.audIr[i]) {
|
if (amiga.audEn[i]) amiga.mustDMA[i]=true;
|
||||||
|
if (amiga.dmaEn && amiga.mustDMA[i] && !amiga.audIr[i]) {
|
||||||
amiga.audTick[i]-=AMIGA_DIVIDER;
|
amiga.audTick[i]-=AMIGA_DIVIDER;
|
||||||
if (amiga.audTick[i]<0) {
|
if (amiga.audTick[i]<0) {
|
||||||
amiga.audTick[i]+=MAX(AMIGA_DIVIDER,amiga.audPer[i]);
|
amiga.audTick[i]+=MAX(AMIGA_DIVIDER,amiga.audPer[i]);
|
||||||
|
@ -114,6 +126,8 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) {
|
||||||
amiga.audWord[i]=!amiga.audWord[i];
|
amiga.audWord[i]=!amiga.audWord[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
amiga.mustDMA[i]=amiga.audEn[i];
|
||||||
|
|
||||||
amiga.audByte[i]=!amiga.audByte[i];
|
amiga.audByte[i]=!amiga.audByte[i];
|
||||||
if (!amiga.audByte[i] && (amiga.useV[i] || amiga.useP[i])) {
|
if (!amiga.audByte[i] && (amiga.useV[i] || amiga.useP[i])) {
|
||||||
amiga.nextOut2[i]=((unsigned char)amiga.audDat[0][i])<<8|((unsigned char)amiga.audDat[1][i]);
|
amiga.nextOut2[i]=((unsigned char)amiga.audDat[0][i])<<8|((unsigned char)amiga.audDat[1][i]);
|
||||||
|
@ -130,7 +144,7 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) {
|
||||||
amiga.audPer[i+1]=amiga.nextOut2[i];
|
amiga.audPer[i+1]=amiga.nextOut2[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if (!amiga.useV[i] && !amiga.useP[i]) {
|
||||||
amiga.nextOut[i]=amiga.audDat[amiga.audByte[i]][i];
|
amiga.nextOut[i]=amiga.audDat[amiga.audByte[i]][i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,12 +168,12 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) {
|
||||||
|
|
||||||
// output
|
// output
|
||||||
if (!isMuted[i]) {
|
if (!isMuted[i]) {
|
||||||
if (amiga.audVol[i]>=64) {
|
if ((amiga.audVol[i]&127)>=64) {
|
||||||
output=amiga.nextOut[i]<<6;
|
output=amiga.nextOut[i]<<6;
|
||||||
} else if (amiga.audVol[i]<=0) {
|
} else if ((amiga.audVol[i]&127)==0) {
|
||||||
output=0;
|
output=0;
|
||||||
} else {
|
} else {
|
||||||
output=amiga.nextOut[i]*volTable[amiga.audVol[i]][amiga.volPos];
|
output=amiga.nextOut[i]*volTable[amiga.audVol[i]&63][amiga.volPos];
|
||||||
}
|
}
|
||||||
if (i==0 || i==3) {
|
if (i==0 || i==3) {
|
||||||
outL+=(output*sep1)>>7;
|
outL+=(output*sep1)>>7;
|
||||||
|
@ -168,7 +182,7 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) {
|
||||||
outL+=(output*sep2)>>7;
|
outL+=(output*sep2)>>7;
|
||||||
outR+=(output*sep1)>>7;
|
outR+=(output*sep1)>>7;
|
||||||
}
|
}
|
||||||
oscBuf[i]->data[oscBuf[i]->needle++]=(amiga.nextOut[i]*MIN(64,amiga.audVol[i]))<<1;
|
oscBuf[i]->data[oscBuf[i]->needle++]=(amiga.nextOut[i]*MIN(64,amiga.audVol[i]&127))<<1;
|
||||||
} else {
|
} else {
|
||||||
oscBuf[i]->data[oscBuf[i]->needle++]=0;
|
oscBuf[i]->data[oscBuf[i]->needle++]=0;
|
||||||
}
|
}
|
||||||
|
@ -190,11 +204,6 @@ void DivPlatformAmiga::irq(int ch) {
|
||||||
if (chan[ch].irLocL==0x400 && chan[ch].irLocH==0 && chan[ch].irLen==1) {
|
if (chan[ch].irLocL==0x400 && chan[ch].irLocH==0 && chan[ch].irLen==1) {
|
||||||
// turn off DMA
|
// turn off DMA
|
||||||
rWrite(0x96,1<<ch);
|
rWrite(0x96,1<<ch);
|
||||||
} else {
|
|
||||||
// write latched loc/len
|
|
||||||
chWrite(ch,0,chan[ch].irLocH);
|
|
||||||
chWrite(ch,2,chan[ch].irLocL);
|
|
||||||
chWrite(ch,4,chan[ch].irLen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// acknowledge interrupt
|
// acknowledge interrupt
|
||||||
|
@ -202,111 +211,96 @@ void DivPlatformAmiga::irq(int ch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#define UPDATE_DMA(x) \
|
#define UPDATE_DMA(x) \
|
||||||
amiga.dmaLen[x]=amiga.audLen[x]; \
|
dmaLen[x]=audLen[x]; \
|
||||||
amiga.dmaLoc[x]=amiga.audLoc[x]; \
|
dmaLoc[x]=audLoc[x]; \
|
||||||
amiga.audByte[x]=true; \
|
audByte[x]=true; \
|
||||||
amiga.audTick[x]=0;
|
audTick[x]=0;
|
||||||
|
|
||||||
void DivPlatformAmiga::rWrite(unsigned short addr, unsigned short val) {
|
void DivPlatformAmiga::Amiga::write(unsigned short addr, unsigned short val) {
|
||||||
if (addr&1) return;
|
if (addr&1) return;
|
||||||
|
|
||||||
//logV("%.3x = %.4x",addr,val);
|
|
||||||
regPool[addr>>1]=val;
|
|
||||||
|
|
||||||
if (!skipRegisterWrites && dumpWrites) {
|
|
||||||
addWrite(addr,val);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (addr&0x1fe) {
|
switch (addr&0x1fe) {
|
||||||
case 0x96: { // DMACON
|
case 0x96: { // DMACON
|
||||||
if (val&32768) {
|
if (val&32768) {
|
||||||
if (val&1) amiga.audEn[0]=true;
|
if (val&1) audEn[0]=true;
|
||||||
if (val&2) amiga.audEn[1]=true;
|
if (val&2) audEn[1]=true;
|
||||||
if (val&4) amiga.audEn[2]=true;
|
if (val&4) audEn[2]=true;
|
||||||
if (val&8) amiga.audEn[3]=true;
|
if (val&8) audEn[3]=true;
|
||||||
if (val&512) amiga.dmaEn=true;
|
if (val&512) dmaEn=true;
|
||||||
} else {
|
} else {
|
||||||
if (val&1) {
|
if (val&1) {
|
||||||
amiga.audEn[0]=false;
|
audEn[0]=false;
|
||||||
UPDATE_DMA(0);
|
|
||||||
}
|
}
|
||||||
if (val&2) {
|
if (val&2) {
|
||||||
amiga.audEn[1]=false;
|
audEn[1]=false;
|
||||||
UPDATE_DMA(1);
|
|
||||||
}
|
}
|
||||||
if (val&4) {
|
if (val&4) {
|
||||||
amiga.audEn[2]=false;
|
audEn[2]=false;
|
||||||
UPDATE_DMA(2);
|
|
||||||
}
|
}
|
||||||
if (val&8) {
|
if (val&8) {
|
||||||
amiga.audEn[3]=false;
|
audEn[3]=false;
|
||||||
UPDATE_DMA(3);
|
|
||||||
}
|
}
|
||||||
if (val&512) {
|
if (val&512) {
|
||||||
amiga.dmaEn=false;
|
dmaEn=false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0x9a: { // INTENA
|
case 0x9a: { // INTENA
|
||||||
if (val&32768) {
|
if (val&32768) {
|
||||||
if (val&128) amiga.audInt[0]=true;
|
if (val&128) audInt[0]=true;
|
||||||
if (val&256) amiga.audInt[1]=true;
|
if (val&256) audInt[1]=true;
|
||||||
if (val&512) amiga.audInt[2]=true;
|
if (val&512) audInt[2]=true;
|
||||||
if (val&1024) amiga.audInt[3]=true;
|
if (val&1024) audInt[3]=true;
|
||||||
} else {
|
} else {
|
||||||
if (val&128) amiga.audInt[0]=false;
|
if (val&128) audInt[0]=false;
|
||||||
if (val&256) amiga.audInt[1]=false;
|
if (val&256) audInt[1]=false;
|
||||||
if (val&512) amiga.audInt[2]=false;
|
if (val&512) audInt[2]=false;
|
||||||
if (val&1024) amiga.audInt[3]=false;
|
if (val&1024) audInt[3]=false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0x9c: { // INTREQ
|
case 0x9c: { // INTREQ
|
||||||
if (val&32768) {
|
if (val&32768) {
|
||||||
if (val&128) {
|
if (val&128) {
|
||||||
amiga.audIr[0]=true;
|
audIr[0]=true;
|
||||||
irq(0);
|
|
||||||
}
|
}
|
||||||
if (val&256) {
|
if (val&256) {
|
||||||
amiga.audIr[1]=true;
|
audIr[1]=true;
|
||||||
irq(1);
|
|
||||||
}
|
}
|
||||||
if (val&512) {
|
if (val&512) {
|
||||||
amiga.audIr[2]=true;
|
audIr[2]=true;
|
||||||
irq(2);
|
|
||||||
}
|
}
|
||||||
if (val&1024) {
|
if (val&1024) {
|
||||||
amiga.audIr[3]=true;
|
audIr[3]=true;
|
||||||
irq(3);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (val&128) amiga.audIr[0]=false;
|
if (val&128) audIr[0]=false;
|
||||||
if (val&256) amiga.audIr[1]=false;
|
if (val&256) audIr[1]=false;
|
||||||
if (val&512) amiga.audIr[2]=false;
|
if (val&512) audIr[2]=false;
|
||||||
if (val&1024) amiga.audIr[3]=false;
|
if (val&1024) audIr[3]=false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0x9e: { // ADKCON
|
case 0x9e: { // ADKCON
|
||||||
if (val&32768) {
|
if (val&32768) {
|
||||||
if (val&1) amiga.useV[0]=true;
|
if (val&1) useV[0]=true;
|
||||||
if (val&2) amiga.useV[1]=true;
|
if (val&2) useV[1]=true;
|
||||||
if (val&4) amiga.useV[2]=true;
|
if (val&4) useV[2]=true;
|
||||||
if (val&8) amiga.useV[3]=true;
|
if (val&8) useV[3]=true;
|
||||||
if (val&16) amiga.useP[0]=true;
|
if (val&16) useP[0]=true;
|
||||||
if (val&32) amiga.useP[1]=true;
|
if (val&32) useP[1]=true;
|
||||||
if (val&64) amiga.useP[2]=true;
|
if (val&64) useP[2]=true;
|
||||||
if (val&128) amiga.useP[3]=true;
|
if (val&128) useP[3]=true;
|
||||||
} else {
|
} else {
|
||||||
if (val&1) amiga.useV[0]=false;
|
if (val&1) useV[0]=false;
|
||||||
if (val&2) amiga.useV[1]=false;
|
if (val&2) useV[1]=false;
|
||||||
if (val&4) amiga.useV[2]=false;
|
if (val&4) useV[2]=false;
|
||||||
if (val&8) amiga.useV[3]=false;
|
if (val&8) useV[3]=false;
|
||||||
if (val&16) amiga.useP[0]=false;
|
if (val&16) useP[0]=false;
|
||||||
if (val&32) amiga.useP[1]=false;
|
if (val&32) useP[1]=false;
|
||||||
if (val&64) amiga.useP[2]=false;
|
if (val&64) useP[2]=false;
|
||||||
if (val&128) amiga.useP[3]=false;
|
if (val&128) useP[3]=false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -316,31 +310,31 @@ void DivPlatformAmiga::rWrite(unsigned short addr, unsigned short val) {
|
||||||
bool updateDMA=false;
|
bool updateDMA=false;
|
||||||
switch (addr&15) {
|
switch (addr&15) {
|
||||||
case 0: // LCH
|
case 0: // LCH
|
||||||
amiga.audLoc[ch]&=0xffff;
|
audLoc[ch]&=0xffff;
|
||||||
amiga.audLoc[ch]|=val<<16;
|
audLoc[ch]|=val<<16;
|
||||||
updateDMA=true;
|
updateDMA=true;
|
||||||
break;
|
break;
|
||||||
case 2: // LCL
|
case 2: // LCL
|
||||||
amiga.audLoc[ch]&=0xffff0000;
|
audLoc[ch]&=0xffff0000;
|
||||||
amiga.audLoc[ch]|=val&0xfffe;
|
audLoc[ch]|=val&0xfffe;
|
||||||
updateDMA=true;
|
updateDMA=true;
|
||||||
break;
|
break;
|
||||||
case 4: // LEN
|
case 4: // LEN
|
||||||
amiga.audLen[ch]=val;
|
audLen[ch]=val;
|
||||||
updateDMA=true;
|
updateDMA=true;
|
||||||
break;
|
break;
|
||||||
case 6: // PER
|
case 6: // PER
|
||||||
amiga.audPer[ch]=val;
|
audPer[ch]=val;
|
||||||
break;
|
break;
|
||||||
case 8: // VOL
|
case 8: // VOL
|
||||||
amiga.audVol[ch]=val;
|
audVol[ch]=val;
|
||||||
break;
|
break;
|
||||||
case 10: // DAT
|
case 10: // DAT
|
||||||
amiga.audDat[0][ch]=val&0xff;
|
audDat[0][ch]=val&0xff;
|
||||||
amiga.audDat[1][ch]=val>>8;
|
audDat[1][ch]=val>>8;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (updateDMA && !amiga.audEn[ch]) {
|
if (updateDMA && !mustDMA[ch]) {
|
||||||
UPDATE_DMA(ch);
|
UPDATE_DMA(ch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -349,6 +343,20 @@ void DivPlatformAmiga::rWrite(unsigned short addr, unsigned short val) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DivPlatformAmiga::rWrite(unsigned short addr, unsigned short val) {
|
||||||
|
if (addr&1) return;
|
||||||
|
|
||||||
|
//logV("%.3x = %.4x",addr,val);
|
||||||
|
if (!skipRegisterWrites) {
|
||||||
|
writes.push(QueuedWrite(addr,val));
|
||||||
|
regPool[addr>>1]=val;
|
||||||
|
|
||||||
|
if (dumpWrites) {
|
||||||
|
addWrite(addr,val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DivPlatformAmiga::updateWave(int ch) {
|
void DivPlatformAmiga::updateWave(int ch) {
|
||||||
for (int i=0; i<MIN(256,(chan[ch].audLen<<1)); i++) {
|
for (int i=0; i<MIN(256,(chan[ch].audLen<<1)); i++) {
|
||||||
sampleMem[(ch<<8)|i]=chan[ch].ws.output[i]^0x80;
|
sampleMem[(ch<<8)|i]=chan[ch].ws.output[i]^0x80;
|
||||||
|
@ -417,6 +425,13 @@ void DivPlatformAmiga::tick(bool sysTick) {
|
||||||
|
|
||||||
if (dmaOff) rWrite(0x96,dmaOff);
|
if (dmaOff) rWrite(0x96,dmaOff);
|
||||||
|
|
||||||
|
for (int i=0; i<4; i++) {
|
||||||
|
if (chan[i].updateWave) {
|
||||||
|
chan[i].updateWave=false;
|
||||||
|
updateWave(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
double off=1.0;
|
double off=1.0;
|
||||||
if (!chan[i].useWave && chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
|
if (!chan[i].useWave && chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
|
||||||
|
@ -509,9 +524,16 @@ void DivPlatformAmiga::tick(bool sysTick) {
|
||||||
if (dmaOn) rWrite(0x96,0x8000|dmaOn);
|
if (dmaOn) rWrite(0x96,0x8000|dmaOn);
|
||||||
|
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
if ((dmaOn&(1<<i)) && !chan[i].useWave && dumpWrites) {
|
if ((dmaOn&(1<<i)) && !chan[i].useWave) {
|
||||||
|
// write latched loc/len
|
||||||
|
if (dumpWrites) {
|
||||||
addWrite(0x200+i,(chan[i].irLocH<<16)|chan[i].irLocL);
|
addWrite(0x200+i,(chan[i].irLocH<<16)|chan[i].irLocL);
|
||||||
addWrite(0x204+i,chan[i].irLen);
|
addWrite(0x204+i,chan[i].irLen);
|
||||||
|
} else {
|
||||||
|
chWrite(i,0,chan[i].irLocH);
|
||||||
|
chWrite(i,2,chan[i].irLocL);
|
||||||
|
chWrite(i,4,chan[i].irLen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -520,10 +542,6 @@ void DivPlatformAmiga::tick(bool sysTick) {
|
||||||
chan[i].writeVol=false;
|
chan[i].writeVol=false;
|
||||||
chWrite(i,8,chan[i].outVol);
|
chWrite(i,8,chan[i].outVol);
|
||||||
}
|
}
|
||||||
if (chan[i].updateWave) {
|
|
||||||
chan[i].updateWave=false;
|
|
||||||
updateWave(i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateADKCon) {
|
if (updateADKCon) {
|
||||||
|
@ -720,6 +738,7 @@ void DivPlatformAmiga::forceIns() {
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
chan[i].insChanged=true;
|
chan[i].insChanged=true;
|
||||||
chan[i].freqChanged=true;
|
chan[i].freqChanged=true;
|
||||||
|
chan[i].writeVol=true;
|
||||||
/*chan[i].keyOn=false;
|
/*chan[i].keyOn=false;
|
||||||
chan[i].keyOff=false;
|
chan[i].keyOff=false;
|
||||||
chan[i].sample=-1;*/
|
chan[i].sample=-1;*/
|
||||||
|
@ -738,6 +757,7 @@ DivDispatchOscBuffer* DivPlatformAmiga::getOscBuffer(int ch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformAmiga::reset() {
|
void DivPlatformAmiga::reset() {
|
||||||
|
writes.clear();
|
||||||
memset(regPool,0,256*sizeof(unsigned short));
|
memset(regPool,0,256*sizeof(unsigned short));
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
chan[i]=DivPlatformAmiga::Channel();
|
chan[i]=DivPlatformAmiga::Channel();
|
||||||
|
@ -750,6 +770,7 @@ void DivPlatformAmiga::reset() {
|
||||||
filterOn=false;
|
filterOn=false;
|
||||||
filtConst=filterOn?filtConstOn:filtConstOff;
|
filtConst=filterOn?filtConstOn:filtConstOff;
|
||||||
updateADKCon=true;
|
updateADKCon=true;
|
||||||
|
delay=0;
|
||||||
|
|
||||||
amiga=Amiga();
|
amiga=Amiga();
|
||||||
// enable DMA
|
// enable DMA
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#define _AMIGA_H
|
#define _AMIGA_H
|
||||||
|
|
||||||
#include "../dispatch.h"
|
#include "../dispatch.h"
|
||||||
|
#include "../../fixedQueue.h"
|
||||||
#include "../waveSynth.h"
|
#include "../waveSynth.h"
|
||||||
|
|
||||||
class DivPlatformAmiga: public DivDispatch {
|
class DivPlatformAmiga: public DivDispatch {
|
||||||
|
@ -59,12 +60,14 @@ class DivPlatformAmiga: public DivDispatch {
|
||||||
bool amigaModel;
|
bool amigaModel;
|
||||||
bool filterOn;
|
bool filterOn;
|
||||||
bool updateADKCon;
|
bool updateADKCon;
|
||||||
|
short delay;
|
||||||
|
|
||||||
struct Amiga {
|
struct Amiga {
|
||||||
// register state
|
// register state
|
||||||
bool audInt[4]; // interrupt on
|
bool audInt[4]; // interrupt on
|
||||||
bool audIr[4]; // interrupt request
|
bool audIr[4]; // interrupt request
|
||||||
bool audEn[4]; // audio DMA on
|
bool audEn[4]; // audio DMA on
|
||||||
|
bool mustDMA[4]; // audio DMA must run
|
||||||
bool useP[4]; // period modulation
|
bool useP[4]; // period modulation
|
||||||
bool useV[4]; // volume modulation
|
bool useV[4]; // volume modulation
|
||||||
|
|
||||||
|
@ -91,6 +94,8 @@ class DivPlatformAmiga: public DivDispatch {
|
||||||
unsigned short hPos; // horizontal position of beam
|
unsigned short hPos; // horizontal position of beam
|
||||||
unsigned char state[4]; // current channel state
|
unsigned char state[4]; // current channel state
|
||||||
|
|
||||||
|
void write(unsigned short addr, unsigned short val);
|
||||||
|
|
||||||
Amiga() {
|
Amiga() {
|
||||||
memset(this,0,sizeof(*this));
|
memset(this,0,sizeof(*this));
|
||||||
}
|
}
|
||||||
|
@ -113,6 +118,14 @@ class DivPlatformAmiga: public DivDispatch {
|
||||||
|
|
||||||
int sep1, sep2;
|
int sep1, sep2;
|
||||||
|
|
||||||
|
struct QueuedWrite {
|
||||||
|
unsigned short addr;
|
||||||
|
unsigned short val;
|
||||||
|
QueuedWrite(): addr(0), val(9) {}
|
||||||
|
QueuedWrite(unsigned short a, unsigned short v): addr(a), val(v) {}
|
||||||
|
};
|
||||||
|
FixedQueue<QueuedWrite,512> writes;
|
||||||
|
|
||||||
friend void putDispatchChip(void*,int);
|
friend void putDispatchChip(void*,int);
|
||||||
friend void putDispatchChan(void*,int,int);
|
friend void putDispatchChan(void*,int,int);
|
||||||
friend class DivExportAmigaValidation;
|
friend class DivExportAmigaValidation;
|
||||||
|
|
|
@ -103,6 +103,8 @@ void FurnaceGUI::doFind() {
|
||||||
int lastRow=e->curSubSong->patLen-1;
|
int lastRow=e->curSubSong->patLen-1;
|
||||||
|
|
||||||
if (curQueryRangeY==1) {
|
if (curQueryRangeY==1) {
|
||||||
|
finishSelection();
|
||||||
|
|
||||||
firstRow=selStart.y;
|
firstRow=selStart.y;
|
||||||
lastRow=selEnd.y;
|
lastRow=selEnd.y;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2325,6 +2325,15 @@ void FurnaceGUI::drawSettings() {
|
||||||
}
|
}
|
||||||
ImGui::Unindent();
|
ImGui::Unindent();
|
||||||
|
|
||||||
|
// SUBSECTION ASSETS
|
||||||
|
CONFIG_SUBSECTION("Assets");
|
||||||
|
|
||||||
|
bool insTypeMenuB=settings.insTypeMenu;
|
||||||
|
if (ImGui::Checkbox("Display instrument type menu when adding instrument",&insTypeMenuB)) {
|
||||||
|
settings.insTypeMenu=insTypeMenuB;
|
||||||
|
settingsChanged=true;
|
||||||
|
}
|
||||||
|
|
||||||
END_SECTION;
|
END_SECTION;
|
||||||
}
|
}
|
||||||
CONFIG_SECTION("Appearance") {
|
CONFIG_SECTION("Appearance") {
|
||||||
|
@ -2888,12 +2897,6 @@ void FurnaceGUI::drawSettings() {
|
||||||
settingsChanged=true;
|
settingsChanged=true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool insTypeMenuB=settings.insTypeMenu;
|
|
||||||
if (ImGui::Checkbox("Display instrument type menu when adding instrument",&insTypeMenuB)) {
|
|
||||||
settings.insTypeMenu=insTypeMenuB;
|
|
||||||
settingsChanged=true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SUBSECTION MACRO EDITOR
|
// SUBSECTION MACRO EDITOR
|
||||||
CONFIG_SUBSECTION("Macro Editor");
|
CONFIG_SUBSECTION("Macro Editor");
|
||||||
ImGui::Text("Macro editor layout:");
|
ImGui::Text("Macro editor layout:");
|
||||||
|
|
Loading…
Reference in a new issue