trunk/src/mess/audio/lynx.c
| r22917 | r22918 | |
| 3 | 3 | ******************************************************************************/ |
| 4 | 4 | |
| 5 | 5 | #include "emu.h" |
| 6 | | #include "includes/lynx.h" |
| 6 | #include "audio/lynx.h" |
| 7 | 7 | |
| 8 | 8 | |
| 9 | 9 | /* accordingly to atari's reference manual |
| r22917 | r22918 | |
| 81 | 81 | |
| 82 | 82 | #define LYNX_AUDIO_CHANNELS 4 |
| 83 | 83 | |
| 84 | | struct LYNX_AUDIO { |
| 85 | | struct { |
| 86 | | INT8 volume; |
| 87 | | UINT8 feedback; |
| 88 | | INT8 output; |
| 89 | | UINT8 shifter; |
| 90 | | UINT8 bakup; |
| 91 | | UINT8 control1; |
| 92 | | UINT8 counter; |
| 93 | | UINT8 control2; |
| 94 | | } reg; |
| 95 | | UINT8 attenuation; |
| 96 | | UINT16 mask; // 12-bit |
| 97 | | UINT16 shifter; // 12-bit |
| 98 | | float ticks; |
| 99 | | int count; |
| 100 | | }; |
| 101 | 84 | |
| 102 | | struct lynx_sound_state |
| 85 | // device type definition |
| 86 | const device_type LYNX_SND = &device_creator<lynx_sound_device>; |
| 87 | const device_type LYNX2_SND = &device_creator<lynx2_sound_device>; |
| 88 | |
| 89 | //************************************************************************** |
| 90 | // LIVE DEVICE |
| 91 | //************************************************************************** |
| 92 | |
| 93 | //------------------------------------------------- |
| 94 | // lynx_sound_device - constructor |
| 95 | //------------------------------------------------- |
| 96 | |
| 97 | lynx_sound_device::lynx_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 98 | : device_t(mconfig, LYNX_SND, "Mikey", tag, owner, clock, "lynx_sound", __FILE__), |
| 99 | device_sound_interface(mconfig, *this) |
| 100 | { |
| 101 | m_timer_delegate = lynx_sound_timer_delegate(); |
| 102 | } |
| 103 | |
| 104 | lynx_sound_device::lynx_sound_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) |
| 105 | : device_t(mconfig, type, name, tag, owner, clock, shortname, source), |
| 106 | device_sound_interface(mconfig, *this) |
| 103 | 107 | { |
| 104 | | sound_stream *mixer_channel; |
| 105 | | float usec_per_sample; |
| 106 | | int *shift_mask; |
| 107 | | int *shift_xor; |
| 108 | | UINT8 attenuation_enable; |
| 109 | | UINT8 master_enable; |
| 110 | | LYNX_AUDIO audio[4]; |
| 111 | | }; |
| 108 | } |
| 112 | 109 | |
| 113 | | INLINE lynx_sound_state *get_safe_token(device_t *device) |
| 110 | |
| 111 | lynx2_sound_device::lynx2_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 112 | : lynx_sound_device(mconfig, LYNX2_SND, "Mikey (Lynx II)", tag, owner, clock, "lynx2_sound", __FILE__) |
| 114 | 113 | { |
| 115 | | assert(device != NULL); |
| 116 | | assert(device->type() == LYNX || device->type() == LYNX2); |
| 117 | | return (lynx_sound_state *)downcast<lynx_sound_device *>(device)->token(); |
| 118 | 114 | } |
| 119 | 115 | |
| 120 | | static void lynx_audio_reset_channel(LYNX_AUDIO *This) |
| 116 | //------------------------------------------------- |
| 117 | // device_config_complete - perform any |
| 118 | // operations now that the configuration is |
| 119 | // complete |
| 120 | //------------------------------------------------- |
| 121 | |
| 122 | void lynx_sound_device::set_timer_delegate(device_t &device, lynx_sound_timer_delegate cb) |
| 121 | 123 | { |
| 122 | | This->reg.volume = 0; |
| 123 | | This->reg.feedback = 0; |
| 124 | | This->reg.output = 0; |
| 125 | | This->reg.shifter = 0; |
| 126 | | This->reg.bakup = 0; |
| 127 | | This->reg.control1 = 0; |
| 128 | | This->reg.counter = 0; |
| 129 | | This->reg.control2 = 0; |
| 124 | lynx_sound_device &dev = downcast<lynx_sound_device &>(device); |
| 125 | dev.m_timer_delegate = cb; |
| 126 | } |
| 130 | 127 | |
| 131 | | This->attenuation = 0; |
| 132 | | This->mask = 0; |
| 133 | | This->shifter = 0; |
| 134 | | This->ticks = 0; |
| 135 | | This->count = 0; |
| 128 | void lynx_sound_device::device_config_complete() |
| 129 | { |
| 136 | 130 | } |
| 137 | 131 | |
| 138 | | void lynx_audio_count_down(device_t *device, int nr) |
| 132 | //------------------------------------------------- |
| 133 | // device_start - device-specific startup |
| 134 | //------------------------------------------------- |
| 135 | |
| 136 | void lynx_sound_device::register_save() |
| 139 | 137 | { |
| 140 | | lynx_sound_state *state = get_safe_token(device); |
| 141 | | LYNX_AUDIO *This=state->audio+nr; |
| 142 | | if (This->reg.control1&8 && (This->reg.control1&7)!=7) return; |
| 143 | | if (nr==0) state->mixer_channel->update(); |
| 144 | | //if ((This->reg.control1&0x0f)==0x0f) //count down if linking enabled and count enabled |
| 145 | | This->count--; |
| 138 | save_item(NAME(m_attenuation_enable)); |
| 139 | save_item(NAME(m_master_enable)); |
| 140 | for (int chan = 0; chan < LYNX_AUDIO_CHANNELS; chan++) |
| 141 | { |
| 142 | state_save_register_item(machine(), "Lynx sound", NULL, chan, m_audio[chan].reg.volume); |
| 143 | state_save_register_item(machine(), "Lynx sound", NULL, chan, m_audio[chan].reg.feedback); |
| 144 | state_save_register_item(machine(), "Lynx sound", NULL, chan, m_audio[chan].reg.output); |
| 145 | state_save_register_item(machine(), "Lynx sound", NULL, chan, m_audio[chan].reg.shifter); |
| 146 | state_save_register_item(machine(), "Lynx sound", NULL, chan, m_audio[chan].reg.bakup); |
| 147 | state_save_register_item(machine(), "Lynx sound", NULL, chan, m_audio[chan].reg.control1); |
| 148 | state_save_register_item(machine(), "Lynx sound", NULL, chan, m_audio[chan].reg.counter); |
| 149 | state_save_register_item(machine(), "Lynx sound", NULL, chan, m_audio[chan].reg.control2); |
| 150 | state_save_register_item(machine(), "Lynx sound", NULL, chan, m_audio[chan].attenuation); |
| 151 | state_save_register_item(machine(), "Lynx sound", NULL, chan, m_audio[chan].mask); |
| 152 | state_save_register_item(machine(), "Lynx sound", NULL, chan, m_audio[chan].shifter); |
| 153 | state_save_register_item(machine(), "Lynx sound", NULL, chan, m_audio[chan].ticks); |
| 154 | state_save_register_item(machine(), "Lynx sound", NULL, chan, m_audio[chan].count); |
| 155 | /* save_item(NAME(m_audio[chan].reg.volume)); |
| 156 | save_item(NAME(m_audio[chan].reg.feedback)); |
| 157 | save_item(NAME(m_audio[chan].reg.output)); |
| 158 | save_item(NAME(m_audio[chan].reg.shifter)); |
| 159 | save_item(NAME(m_audio[chan].reg.bakup)); |
| 160 | save_item(NAME(m_audio[chan].reg.control1)); |
| 161 | save_item(NAME(m_audio[chan].reg.counter)); |
| 162 | save_item(NAME(m_audio[chan].reg.control2)); |
| 163 | save_item(NAME(m_audio[chan].attenuation)); |
| 164 | save_item(NAME(m_audio[chan].mask)); |
| 165 | save_item(NAME(m_audio[chan].shifter)); |
| 166 | save_item(NAME(m_audio[chan].ticks)); |
| 167 | save_item(NAME(m_audio[chan].count));*/ |
| 168 | } |
| 146 | 169 | } |
| 147 | 170 | |
| 148 | | static void lynx_audio_shift(device_t *device, LYNX_AUDIO *channel) |
| 171 | void lynx_sound_device::init() |
| 172 | { |
| 173 | m_shift_mask = auto_alloc_array_clear(machine(), int, 512); |
| 174 | m_shift_xor = auto_alloc_array_clear(machine(), int, 4096); |
| 175 | |
| 176 | for (int i = 0; i < 512; i++) |
| 177 | { |
| 178 | m_shift_mask[i] = 0; |
| 179 | if (i & 1) m_shift_mask[i] |= 1; |
| 180 | if (i & 2) m_shift_mask[i] |= 2; |
| 181 | if (i & 4) m_shift_mask[i] |= 4; |
| 182 | if (i & 8) m_shift_mask[i] |= 8; |
| 183 | if (i & 0x10) m_shift_mask[i] |= 0x10; |
| 184 | if (i & 0x20) m_shift_mask[i] |= 0x20; |
| 185 | if (i & 0x40) m_shift_mask[i] |= 0x400; |
| 186 | if (i & 0x80) m_shift_mask[i] |= 0x800; |
| 187 | if (i & 0x100) m_shift_mask[i] |= 0x80; |
| 188 | } |
| 189 | |
| 190 | for (int i = 0; i < 4096; i++) |
| 191 | { |
| 192 | m_shift_xor[i] = 1; |
| 193 | for (int j = 4096/2; j > 0; j >>= 1) |
| 194 | { |
| 195 | if (i & j) |
| 196 | m_shift_xor[i] ^= 1; |
| 197 | } |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | void lynx_sound_device::device_start() |
| 149 | 202 | { |
| 150 | | lynx_sound_state *state = get_safe_token(device); |
| 203 | m_mixer_channel = machine().sound().stream_alloc(*this, 0, 1, machine().sample_rate(), this); |
| 204 | m_usec_per_sample = 1000000 / machine().sample_rate(); |
| 205 | m_timer_delegate.bind_relative_to(*owner()); |
| 206 | init(); |
| 207 | register_save(); |
| 208 | } |
| 209 | |
| 210 | |
| 211 | void lynx2_sound_device::device_start() |
| 212 | { |
| 213 | m_mixer_channel = machine().sound().stream_alloc(*this, 0, 2, machine().sample_rate(), this); |
| 214 | m_usec_per_sample = 1000000 / machine().sample_rate(); |
| 215 | m_timer_delegate.bind_relative_to(*owner()); |
| 216 | init(); |
| 217 | register_save(); |
| 218 | } |
| 219 | |
| 220 | //------------------------------------------------- |
| 221 | // device_reset - device-specific reset |
| 222 | //------------------------------------------------- |
| 223 | |
| 224 | void lynx_sound_device::device_reset() |
| 225 | { |
| 226 | for (int i = 0; i < LYNX_AUDIO_CHANNELS; i++) |
| 227 | { |
| 228 | reset_channel(&m_audio[i]); |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | |
| 233 | void lynx_sound_device::reset_channel(LYNX_AUDIO *channel) |
| 234 | { |
| 235 | channel->reg.volume = 0; |
| 236 | channel->reg.feedback = 0; |
| 237 | channel->reg.output = 0; |
| 238 | channel->reg.shifter = 0; |
| 239 | channel->reg.bakup = 0; |
| 240 | channel->reg.control1 = 0; |
| 241 | channel->reg.counter = 0; |
| 242 | channel->reg.control2 = 0; |
| 243 | |
| 244 | channel->attenuation = 0; |
| 245 | channel->mask = 0; |
| 246 | channel->shifter = 0; |
| 247 | channel->ticks = 0; |
| 248 | channel->count = 0; |
| 249 | } |
| 250 | |
| 251 | void lynx_sound_device::count_down(int nr) |
| 252 | { |
| 253 | LYNX_AUDIO *channel = &m_audio[nr]; |
| 254 | if (channel->reg.control1 & 8 && (channel->reg.control1 & 7) != 7) |
| 255 | return; |
| 256 | if (nr == 0) |
| 257 | m_mixer_channel->update(); |
| 258 | //if ((channel->reg.control1 & 0x0f) == 0x0f) //count down if linking enabled and count enabled |
| 259 | channel->count--; |
| 260 | } |
| 261 | |
| 262 | void lynx_sound_device::shift(int chan_nr) |
| 263 | { |
| 151 | 264 | INT16 out_temp; |
| 152 | | UINT8 channel_number = (UINT8)(channel - state->audio); |
| 265 | LYNX_AUDIO *channel; |
| 266 | |
| 267 | assert(chan_nr < 4); |
| 153 | 268 | |
| 154 | | //channel->shifter = ((channel->shifter<<1)&0xffe) | (state->shift_xor[ channel->shifter & channel->mask ]&1); |
| 269 | channel = &m_audio[chan_nr]; |
| 270 | //channel->shifter = ((channel->shifter<<1)&0xffe) | (m_shift_xor[ channel->shifter & channel->mask ]&1); |
| 155 | 271 | |
| 156 | | // alternative method (functionally the same as above) |
| 157 | | UINT8 xor_out=0; |
| 158 | | for(int bit=0;bit<12;bit++) |
| 272 | // alternative method (functionally the same as above) |
| 273 | UINT8 xor_out = 0; |
| 274 | for (int bit = 0; bit < 12; bit++) |
| 159 | 275 | { |
| 160 | | if((channel->mask>>bit)&1) xor_out ^= (channel->shifter>>bit)&1; |
| 276 | if ((channel->mask >> bit) & 1) |
| 277 | xor_out ^= (channel->shifter >> bit) & 1; |
| 161 | 278 | } |
| 162 | | channel->shifter = ((channel->shifter<<1)&0xffe) | (xor_out ^ 1); // output of xor is inverted |
| 279 | channel->shifter = ((channel->shifter << 1) & 0xffe) | (xor_out ^ 1); // output of xor is inverted |
| 163 | 280 | |
| 164 | 281 | |
| 165 | | if (channel->reg.control1&0x20) // integrate mode enabled |
| 282 | if (channel->reg.control1 & 0x20) // integrate mode enabled |
| 166 | 283 | { |
| 167 | | if (channel->shifter&1) |
| 168 | | { |
| 284 | if (channel->shifter & 1) |
| 169 | 285 | out_temp = channel->reg.output + channel->reg.volume; |
| 170 | | } |
| 171 | 286 | else |
| 172 | | { |
| 173 | 287 | out_temp = channel->reg.output - channel->reg.volume; |
| 174 | | } |
| 175 | 288 | |
| 176 | 289 | // clipping |
| 177 | | if(out_temp > 127) out_temp = 127; |
| 178 | | if(out_temp < -128) out_temp = -128; |
| 290 | if (out_temp > 127) out_temp = 127; |
| 291 | if (out_temp < -128) out_temp = -128; |
| 179 | 292 | channel->reg.output = (INT16)out_temp; |
| 180 | 293 | } |
| 181 | | lynx_state *drvstate = device->machine().driver_data<lynx_state>(); |
| 182 | | switch (channel_number) |
| 294 | |
| 295 | switch (chan_nr) |
| 183 | 296 | { |
| 184 | | case 0: lynx_audio_count_down(device, 1); break; |
| 185 | | case 1: lynx_audio_count_down(device, 2); break; |
| 186 | | case 2: lynx_audio_count_down(device, 3); break; |
| 187 | | case 3: drvstate->lynx_timer_count_down(1); break; |
| 188 | | default: logerror("Invalid channel number %d\n", channel_number); break; |
| 297 | case 0: count_down(1); break; |
| 298 | case 1: count_down(2); break; |
| 299 | case 2: count_down(3); break; |
| 300 | case 3: |
| 301 | if (!m_timer_delegate.isnull()) |
| 302 | m_timer_delegate(); |
| 303 | break; |
| 189 | 304 | } |
| 190 | 305 | } |
| 191 | 306 | |
| 192 | | static void lynx_audio_execute(device_t *device, LYNX_AUDIO *channel) |
| 307 | void lynx_sound_device::execute(int chan_nr) |
| 193 | 308 | { |
| 194 | | lynx_sound_state *state = get_safe_token(device); |
| 195 | | if (channel->reg.control1&8) // count enable |
| 309 | LYNX_AUDIO *channel; |
| 310 | |
| 311 | assert(chan_nr < 4); |
| 312 | |
| 313 | channel = &m_audio[chan_nr]; |
| 314 | |
| 315 | if (channel->reg.control1 & 8) // count enable |
| 196 | 316 | { |
| 197 | | channel->ticks+=state->usec_per_sample; |
| 198 | | if ((channel->reg.control1&7)==7) // link |
| 317 | channel->ticks += m_usec_per_sample; |
| 318 | if ((channel->reg.control1 & 7) == 7) // link |
| 199 | 319 | { |
| 200 | | if (channel->count<0) // counter finished |
| 320 | if (channel->count < 0) // counter finished |
| 201 | 321 | { |
| 202 | 322 | //channel->count+=channel->reg.counter; // reload (wrong?) |
| 203 | | if (channel->reg.control1&0x10) |
| 323 | if (channel->reg.control1 & 0x10) |
| 204 | 324 | channel->count = channel->reg.bakup; |
| 205 | | lynx_audio_shift(device, channel); |
| 325 | shift(chan_nr); |
| 206 | 326 | } |
| 207 | 327 | } |
| 208 | 328 | else |
| 209 | 329 | { |
| 210 | | int t=1<<(channel->reg.control1&7); // microseconds per count |
| 330 | int t = 1 << (channel->reg.control1 & 7); // microseconds per count |
| 211 | 331 | for (;;) |
| 212 | 332 | { |
| 213 | | for (;(channel->ticks >= t) && (channel->count >= 0); channel->ticks-=t) // at least one sampled worth of time left, timer not expired |
| 214 | | { |
| 333 | for (; (channel->ticks >= t) && (channel->count >= 0); channel->ticks -= t) // at least one sampled worth of time left, timer not expired |
| 215 | 334 | channel->count--; |
| 216 | | } |
| 217 | | if (channel->ticks<t) break; |
| 218 | | if (channel->count<0) |
| 335 | |
| 336 | if (channel->ticks < t) |
| 337 | break; |
| 338 | |
| 339 | if (channel->count < 0) |
| 219 | 340 | { |
| 220 | | lynx_audio_shift(device, channel); |
| 221 | | if (channel->reg.control1&0x10) |
| 341 | shift(chan_nr); |
| 342 | if (channel->reg.control1 & 0x10) |
| 222 | 343 | channel->count = channel->reg.bakup; |
| 223 | 344 | else |
| 224 | 345 | break; |
| 225 | 346 | } |
| 226 | 347 | } |
| 227 | 348 | } |
| 228 | | if (!(channel->reg.control1&0x20)) // normal mode |
| 349 | |
| 350 | if (!(channel->reg.control1 & 0x20)) // normal mode |
| 229 | 351 | { |
| 230 | 352 | channel->reg.output = (channel->shifter & 1) ? channel->reg.volume : -channel->reg.volume; |
| 231 | 353 | } |
| 232 | 354 | } |
| 233 | 355 | else |
| 234 | 356 | { |
| 235 | | channel->ticks=0; |
| 236 | | channel->count=0; |
| 357 | channel->ticks = 0; |
| 358 | channel->count = 0; |
| 237 | 359 | } |
| 238 | 360 | } |
| 239 | 361 | |
| 240 | | |
| 241 | | UINT8 lynx_audio_read(device_t *device, int offset) |
| 362 | READ8_MEMBER(lynx_sound_device::read) |
| 242 | 363 | { |
| 243 | | lynx_sound_state *state = get_safe_token(device); |
| 244 | | UINT8 value=0; |
| 245 | | LYNX_AUDIO *channel=&state->audio[(offset>>3)&3]; |
| 246 | | state->mixer_channel->update(); |
| 364 | UINT8 value = 0; |
| 365 | LYNX_AUDIO *channel = &m_audio[(offset >> 3) & 3]; |
| 366 | |
| 367 | m_mixer_channel->update(); |
| 368 | |
| 247 | 369 | if (offset < 0x40) |
| 248 | 370 | { |
| 249 | | switch (offset&7) { |
| 371 | switch (offset & 7) |
| 372 | { |
| 250 | 373 | case 0: |
| 251 | 374 | value = channel->reg.volume; |
| 252 | 375 | break; |
| r22917 | r22918 | |
| 258 | 381 | break; |
| 259 | 382 | case 3: |
| 260 | 383 | // current shifter state (lower 8 bits) |
| 261 | | value = channel->shifter&0xff; |
| 384 | value = channel->shifter & 0xff; |
| 262 | 385 | break; |
| 263 | 386 | case 4: |
| 264 | 387 | value = channel->reg.bakup; |
| r22917 | r22918 | |
| 275 | 398 | break; |
| 276 | 399 | case 7: |
| 277 | 400 | // current shifter state (upper 4 bits), status bits |
| 278 | | value = (channel->shifter>>4)&0xf0; |
| 279 | | value |= channel->reg.control2&0x0f; |
| 401 | value = (channel->shifter >> 4) & 0xf0; |
| 402 | value |= channel->reg.control2 & 0x0f; |
| 280 | 403 | break; |
| 281 | 404 | } |
| 282 | 405 | } |
| r22917 | r22918 | |
| 285 | 408 | switch (offset) // Lynx II stereo control registers |
| 286 | 409 | { |
| 287 | 410 | case 0x40: case 0x41: case 0x42: case 0x43: |
| 288 | | value = state->audio[offset&3].attenuation; |
| 411 | value = m_audio[offset & 3].attenuation; |
| 289 | 412 | break; |
| 290 | 413 | case 0x44: |
| 291 | | value = state->attenuation_enable; |
| 414 | value = m_attenuation_enable; |
| 292 | 415 | break; |
| 293 | 416 | case 0x50: |
| 294 | | value = state->master_enable; |
| 417 | value = m_master_enable; |
| 295 | 418 | break; |
| 296 | 419 | } |
| 297 | 420 | |
| r22917 | r22918 | |
| 299 | 422 | return value; |
| 300 | 423 | } |
| 301 | 424 | |
| 302 | | void lynx_audio_write(device_t *device, int offset, UINT8 data) |
| 425 | WRITE8_MEMBER(lynx_sound_device::write) |
| 303 | 426 | { |
| 304 | | lynx_sound_state *state = get_safe_token(device); |
| 305 | 427 | //logerror("audio write %.2x %.2x\n", offset, data); |
| 306 | | LYNX_AUDIO *channel=&state->audio[(offset>>3)&3]; |
| 307 | | state->mixer_channel->update(); |
| 428 | LYNX_AUDIO *channel = &m_audio[(offset >> 3) & 3]; |
| 429 | |
| 430 | m_mixer_channel->update(); |
| 431 | |
| 308 | 432 | if (offset < 0x40) |
| 309 | 433 | { |
| 310 | | switch (offset & 0x07) { |
| 434 | switch (offset & 0x07) |
| 435 | { |
| 311 | 436 | // Volume control (signed) |
| 312 | 437 | case 0: |
| 313 | 438 | channel->reg.volume = data; |
| r22917 | r22918 | |
| 356 | 481 | switch (offset) // Stereo Registers |
| 357 | 482 | { |
| 358 | 483 | case 0x40: case 0x41: case 0x42: case 0x43: |
| 359 | | state->audio[offset&3].attenuation=data; |
| 484 | m_audio[offset&3].attenuation = data; |
| 360 | 485 | break; |
| 361 | 486 | case 0x44: |
| 362 | | state->attenuation_enable=data; |
| 487 | m_attenuation_enable = data; |
| 363 | 488 | break; |
| 364 | 489 | case 0x50: |
| 365 | | state->master_enable=data; |
| 490 | m_master_enable = data; |
| 366 | 491 | break; |
| 367 | 492 | } |
| 368 | 493 | } |
| 369 | 494 | } |
| 370 | 495 | |
| 371 | | /************************************/ |
| 372 | | /* Sound handler update */ |
| 373 | | /************************************/ |
| 374 | | static STREAM_UPDATE( lynx_update ) |
| 496 | |
| 497 | //------------------------------------------------- |
| 498 | // sound_stream_update - handle a stream update |
| 499 | //------------------------------------------------- |
| 500 | |
| 501 | void lynx_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 375 | 502 | { |
| 376 | | lynx_sound_state *state = get_safe_token(device); |
| 377 | | int i, channel; |
| 378 | | //LYNX_AUDIO *channel; |
| 379 | 503 | int v; |
| 380 | 504 | stream_sample_t *buffer = outputs[0]; |
| 381 | | |
| 382 | | for (i = 0; i < samples; i++, buffer++) |
| 505 | |
| 506 | for (int i = 0; i < samples; i++, buffer++) |
| 383 | 507 | { |
| 384 | 508 | *buffer = 0; |
| 385 | | for (channel=0; channel<LYNX_AUDIO_CHANNELS; channel++) |
| 509 | for (int channel = 0; channel < LYNX_AUDIO_CHANNELS; channel++) |
| 386 | 510 | { |
| 387 | | lynx_audio_execute(device, &state->audio[channel]); |
| 388 | | v=state->audio[channel].reg.output; |
| 389 | | *buffer+=v*15; // where does the *15 come from? |
| 511 | execute(channel); |
| 512 | v = m_audio[channel].reg.output; |
| 513 | *buffer += v * 15; // where does the *15 come from? |
| 390 | 514 | } |
| 391 | 515 | } |
| 392 | 516 | } |
| 393 | 517 | |
| 394 | | static STREAM_UPDATE( lynx2_update ) |
| 518 | //------------------------------------------------- |
| 519 | // sound_stream_update - handle a stream update |
| 520 | //------------------------------------------------- |
| 521 | |
| 522 | void lynx2_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 395 | 523 | { |
| 396 | | lynx_sound_state *state = get_safe_token(device); |
| 397 | 524 | stream_sample_t *left=outputs[0], *right=outputs[1]; |
| 398 | | int i, j; |
| 399 | | LYNX_AUDIO *channel; |
| 400 | 525 | int v; |
| 401 | | |
| 402 | | for (i = 0; i < samples; i++, left++, right++) |
| 526 | |
| 527 | for (int i = 0; i < samples; i++, left++, right++) |
| 403 | 528 | { |
| 404 | 529 | *left = 0; |
| 405 | 530 | *right= 0; |
| 406 | | for (channel=state->audio, j=0; j<ARRAY_LENGTH(state->audio); j++, channel++) |
| 531 | for (int channel = 0; channel < LYNX_AUDIO_CHANNELS; channel++) |
| 407 | 532 | { |
| 408 | | lynx_audio_execute(device, channel); |
| 409 | | v=channel->reg.output; |
| 410 | | if (!(state->master_enable&(0x10<<j))) |
| 533 | execute(channel); |
| 534 | v = m_audio[channel].reg.output; |
| 535 | if (!(m_master_enable & (0x10 << channel))) |
| 411 | 536 | { |
| 412 | | if (state->attenuation_enable&(0x10<<j)) { |
| 413 | | *left+=v*(channel->attenuation>>4); |
| 414 | | } else { |
| 415 | | *left+=v*15; |
| 416 | | } |
| 537 | if (m_attenuation_enable & (0x10 << channel)) |
| 538 | *left += v * (m_audio[channel].attenuation >> 4); |
| 539 | else |
| 540 | *left += v * 15; |
| 417 | 541 | } |
| 418 | | if (!(state->master_enable&(1<<j))) |
| 542 | if (!(m_master_enable & (1 << channel))) |
| 419 | 543 | { |
| 420 | | if (state->attenuation_enable&(1<<j)) { |
| 421 | | *right+=v*(channel->attenuation&0xf); |
| 422 | | } else { |
| 423 | | *right+=v*15; |
| 424 | | } |
| 544 | if (m_attenuation_enable & (1 << channel)) |
| 545 | *right += v * (m_audio[channel].attenuation & 0xf); |
| 546 | else |
| 547 | *right += v * 15; |
| 425 | 548 | } |
| 426 | 549 | } |
| 427 | 550 | } |
| 428 | 551 | } |
| 429 | | |
| 430 | | static void lynx_audio_init(device_t *device) |
| 431 | | { |
| 432 | | lynx_sound_state *state = get_safe_token(device); |
| 433 | | int i; |
| 434 | | state->shift_mask = auto_alloc_array(device->machine(), int, 512); |
| 435 | | assert(state->shift_mask); |
| 436 | | |
| 437 | | state->shift_xor = auto_alloc_array(device->machine(), int, 4096); |
| 438 | | assert(state->shift_xor); |
| 439 | | |
| 440 | | for (i=0; i<512; i++) |
| 441 | | { |
| 442 | | state->shift_mask[i]=0; |
| 443 | | if (i&1) state->shift_mask[i]|=1; |
| 444 | | if (i&2) state->shift_mask[i]|=2; |
| 445 | | if (i&4) state->shift_mask[i]|=4; |
| 446 | | if (i&8) state->shift_mask[i]|=8; |
| 447 | | if (i&0x10) state->shift_mask[i]|=0x10; |
| 448 | | if (i&0x20) state->shift_mask[i]|=0x20; |
| 449 | | if (i&0x40) state->shift_mask[i]|=0x400; |
| 450 | | if (i&0x80) state->shift_mask[i]|=0x800; |
| 451 | | if (i&0x100) state->shift_mask[i]|=0x80; |
| 452 | | } |
| 453 | | for (i=0; i<4096; i++) |
| 454 | | { |
| 455 | | int j; |
| 456 | | state->shift_xor[i]=1; |
| 457 | | for (j=4096/2; j>0; j>>=1) |
| 458 | | { |
| 459 | | if (i & j) |
| 460 | | state->shift_xor[i] ^= 1; |
| 461 | | } |
| 462 | | } |
| 463 | | } |
| 464 | | |
| 465 | | static DEVICE_RESET( lynx_sound ) |
| 466 | | { |
| 467 | | lynx_sound_state *state = get_safe_token(device); |
| 468 | | int i; |
| 469 | | for (i=0; i<ARRAY_LENGTH(state->audio); i++) |
| 470 | | { |
| 471 | | lynx_audio_reset_channel(state->audio+i); |
| 472 | | } |
| 473 | | } |
| 474 | | |
| 475 | | |
| 476 | | |
| 477 | | /************************************/ |
| 478 | | /* Sound handler start */ |
| 479 | | /************************************/ |
| 480 | | |
| 481 | | static DEVICE_START(lynx_sound) |
| 482 | | { |
| 483 | | lynx_sound_state *state = get_safe_token(device); |
| 484 | | state->mixer_channel = device->machine().sound().stream_alloc(*device, 0, 1, device->machine().sample_rate(), 0, lynx_update); |
| 485 | | |
| 486 | | state->usec_per_sample = 1000000 / device->machine().sample_rate(); |
| 487 | | |
| 488 | | lynx_audio_init(device); |
| 489 | | } |
| 490 | | |
| 491 | | |
| 492 | | static DEVICE_START(lynx2_sound) |
| 493 | | { |
| 494 | | lynx_sound_state *state = get_safe_token(device); |
| 495 | | state->mixer_channel = device->machine().sound().stream_alloc(*device, 0, 2, device->machine().sample_rate(), 0, lynx2_update); |
| 496 | | |
| 497 | | state->usec_per_sample = 1000000 / device->machine().sample_rate(); |
| 498 | | |
| 499 | | lynx_audio_init(device); |
| 500 | | } |
| 501 | | |
| 502 | | |
| 503 | | const device_type LYNX = &device_creator<lynx_sound_device>; |
| 504 | | |
| 505 | | lynx_sound_device::lynx_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 506 | | : device_t(mconfig, LYNX, "Mikey", tag, owner, clock), |
| 507 | | device_sound_interface(mconfig, *this) |
| 508 | | { |
| 509 | | m_token = global_alloc_clear(lynx_sound_state); |
| 510 | | } |
| 511 | | |
| 512 | | lynx_sound_device::lynx_sound_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) |
| 513 | | : device_t(mconfig, type, name, tag, owner, clock, shortname, source), |
| 514 | | device_sound_interface(mconfig, *this) |
| 515 | | { |
| 516 | | m_token = global_alloc_clear(lynx_sound_state); |
| 517 | | } |
| 518 | | |
| 519 | | //------------------------------------------------- |
| 520 | | // device_config_complete - perform any |
| 521 | | // operations now that the configuration is |
| 522 | | // complete |
| 523 | | //------------------------------------------------- |
| 524 | | |
| 525 | | void lynx_sound_device::device_config_complete() |
| 526 | | { |
| 527 | | } |
| 528 | | |
| 529 | | //------------------------------------------------- |
| 530 | | // device_start - device-specific startup |
| 531 | | //------------------------------------------------- |
| 532 | | |
| 533 | | void lynx_sound_device::device_start() |
| 534 | | { |
| 535 | | DEVICE_START_NAME( lynx_sound )(this); |
| 536 | | } |
| 537 | | |
| 538 | | //------------------------------------------------- |
| 539 | | // device_reset - device-specific reset |
| 540 | | //------------------------------------------------- |
| 541 | | |
| 542 | | void lynx_sound_device::device_reset() |
| 543 | | { |
| 544 | | DEVICE_RESET_NAME( lynx_sound )(this); |
| 545 | | } |
| 546 | | |
| 547 | | //------------------------------------------------- |
| 548 | | // sound_stream_update - handle a stream update |
| 549 | | //------------------------------------------------- |
| 550 | | |
| 551 | | void lynx_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 552 | | { |
| 553 | | // should never get here |
| 554 | | fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); |
| 555 | | } |
| 556 | | |
| 557 | | |
| 558 | | const device_type LYNX2 = &device_creator<lynx2_sound_device>; |
| 559 | | |
| 560 | | lynx2_sound_device::lynx2_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 561 | | : lynx_sound_device(mconfig, LYNX2, "Mikey (Lynx II)", tag, owner, clock, "lynx2_sound", __FILE__) |
| 562 | | { |
| 563 | | } |
| 564 | | |
| 565 | | void lynx2_sound_device::device_start() |
| 566 | | { |
| 567 | | DEVICE_START_NAME( lynx2_sound )(this); |
| 568 | | } |
| 569 | | |
| 570 | | //------------------------------------------------- |
| 571 | | // sound_stream_update - handle a stream update |
| 572 | | //------------------------------------------------- |
| 573 | | |
| 574 | | void lynx2_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 575 | | { |
| 576 | | // should never get here |
| 577 | | fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); |
| 578 | | } |
trunk/src/mess/machine/lynx.c
| r22917 | r22918 | |
| 20 | 20 | |
| 21 | 21 | ****************************************/ |
| 22 | 22 | |
| 23 | | /* |
| 24 | | 2008-10 FP: |
| 25 | | Current implementation: lynx_blitter reads what will be drawn and sets which line_functions to use. |
| 26 | | It then calls lynx_blit_lines which sets the various flip bits (horizontal and vertical) and calls |
| 27 | | the chosen line_function. These functions (available in various versions, depending on how many |
| 28 | | color bits are to be used) finally call lynx_plot_pixel which draws the sprite. |
| 29 | | |
| 30 | | Notice however that, based on the problems in Electrocop, Jimmy Connors Tennis and Switchblade II |
| 31 | | (among the others), it clearly seems that something is being lost in some of the passages. From |
| 32 | | my partial understanding while comparing the code with the manual, I would suspect the loops in |
| 33 | | the line_functions to be not completely correct. |
| 34 | | |
| 35 | | This whole part will be eventually moved to video/ once I'm satisfied (or I give up). |
| 36 | | */ |
| 37 | | |
| 38 | 23 | /* modes from blitter command */ |
| 39 | 24 | enum { |
| 40 | 25 | BACKGROUND = 0, |
| r22917 | r22918 | |
| 413 | 398 | } |
| 414 | 399 | } |
| 415 | 400 | |
| 416 | | static void lynx_blit_2color_line(lynx_state *state, const int y, const int xdir) {state->lynx_blit_do_work(y, xdir, 1, 0x01);} |
| 417 | | static void lynx_blit_4color_line(lynx_state *state, const int y, const int xdir) {state->lynx_blit_do_work(y, xdir, 2, 0x03);} |
| 418 | | static void lynx_blit_8color_line(lynx_state *state, const int y, const int xdir) {state->lynx_blit_do_work(y, xdir, 3, 0x07);} |
| 419 | | static void lynx_blit_16color_line(lynx_state *state, const int y, const int xdir) {state->lynx_blit_do_work(y, xdir, 4, 0x0f);} |
| 420 | | |
| 421 | 401 | void lynx_state::lynx_blit_rle_do_work( const INT16 y, const int xdir, const int bits_per_pixel, const int mask ) |
| 422 | 402 | { |
| 423 | 403 | int i; |
| r22917 | r22918 | |
| 491 | 471 | for ( ; count>=0; count--) |
| 492 | 472 | { |
| 493 | 473 | width_accum += m_blitter.width; |
| 494 | | for (i = 0; i < (width_accum>>8); i++, xi += xdir) |
| 474 | for (i = 0; i < (width_accum >> 8); i++, xi += xdir) |
| 495 | 475 | { |
| 496 | 476 | if ((xi >= 0) && (xi < 160)) |
| 497 | 477 | lynx_plot_pixel(m_blitter.mode, xi, y, color); |
| r22917 | r22918 | |
| 502 | 482 | } |
| 503 | 483 | } |
| 504 | 484 | |
| 505 | | static void lynx_blit_2color_rle_line(lynx_state *state, const int y, const int xdir) {state->lynx_blit_rle_do_work(y, xdir, 1, 0x01);} |
| 506 | | static void lynx_blit_4color_rle_line(lynx_state *state, const int y, const int xdir) {state->lynx_blit_rle_do_work(y, xdir, 2, 0x03);} |
| 507 | | static void lynx_blit_8color_rle_line(lynx_state *state, const int y, const int xdir) {state->lynx_blit_rle_do_work(y, xdir, 3, 0x07);} |
| 508 | | static void lynx_blit_16color_rle_line(lynx_state *state, const int y, const int xdir) {state->lynx_blit_rle_do_work(y, xdir, 4, 0x0f);} |
| 509 | | |
| 510 | 485 | void lynx_state::lynx_blit_lines() |
| 511 | 486 | { |
| 487 | static const int lynx_color_masks[4] = { 0x01, 0x03, 0x07, 0x0f }; |
| 488 | INT16 y; |
| 512 | 489 | int i; |
| 513 | | INT16 y; |
| 514 | 490 | int ydir = 0, xdir = 0; |
| 515 | 491 | int flip = 0; |
| 516 | 492 | |
| r22917 | r22918 | |
| 580 | 556 | continue; |
| 581 | 557 | } |
| 582 | 558 | |
| 583 | | |
| 584 | 559 | m_blitter.height_accumulator += m_blitter.height; |
| 585 | | for (int i=0; i < (m_blitter.height_accumulator>>8); i++, y += ydir) |
| 586 | | { |
| 560 | for (int j = 0; j < (m_blitter.height_accumulator >> 8); j++, y += ydir) |
| 561 | { |
| 587 | 562 | if (y >= 0 && y < 102) |
| 588 | | m_blitter.line_function(this, y, xdir); |
| 563 | { |
| 564 | if (m_blitter.use_rle) |
| 565 | lynx_blit_rle_do_work(y, xdir, m_blitter.line_color + 1, lynx_color_masks[m_blitter.line_color]); |
| 566 | else |
| 567 | lynx_blit_do_work(y, xdir, m_blitter.line_color + 1, lynx_color_masks[m_blitter.line_color]); |
| 568 | } |
| 589 | 569 | m_blitter.width += (INT16)m_blitter.stretch; |
| 590 | 570 | if (m_blitter.vstretch) // doesn't seem to be used |
| 591 | 571 | { |
| r22917 | r22918 | |
| 681 | 661 | |
| 682 | 662 | void lynx_state::lynx_blitter() |
| 683 | 663 | { |
| 684 | | static const int lynx_colors[4]={2,4,8,16}; |
| 664 | static const int lynx_colors[4] = { 2, 4, 8, 16 }; |
| 685 | 665 | UINT8 palette_offset; |
| 686 | 666 | UINT8 coldep; |
| 667 | int colors; |
| 687 | 668 | |
| 688 | | static void (* const blit_line[4])(lynx_state *state, const int y, const int xdir)= { |
| 689 | | lynx_blit_2color_line, |
| 690 | | lynx_blit_4color_line, |
| 691 | | lynx_blit_8color_line, |
| 692 | | lynx_blit_16color_line |
| 693 | | }; |
| 694 | | |
| 695 | | static void (* const blit_rle_line[4])(lynx_state *state, const int y, const int xdir)= { |
| 696 | | lynx_blit_2color_rle_line, |
| 697 | | lynx_blit_4color_rle_line, |
| 698 | | lynx_blit_8color_rle_line, |
| 699 | | lynx_blit_16color_rle_line |
| 700 | | }; |
| 701 | | |
| 702 | | int i; int colors; |
| 703 | | |
| 704 | | m_blitter.mem = (UINT8*)m_maincpu->space(AS_PROGRAM).get_read_ptr(0x0000); |
| 705 | | |
| 706 | 669 | m_blitter.busy = 1; // blitter working |
| 707 | 670 | m_blitter.memory_accesses = 0; |
| 708 | 671 | |
| r22917 | r22918 | |
| 750 | 713 | |
| 751 | 714 | colors = lynx_colors[m_blitter.spr_ctl0 >> 6]; |
| 752 | 715 | |
| 753 | | for (i = 0; i < colors / 2; i++) |
| 716 | for (int i = 0; i < colors / 2; i++) |
| 754 | 717 | { |
| 755 | 718 | m_blitter.color[i * 2] = lynx_read_ram(m_blitter.scb + palette_offset + i) >> 4; |
| 756 | 719 | m_blitter.color[i * 2 + 1 ] = lynx_read_ram(m_blitter.scb + palette_offset + i) & 0x0f; |
| r22917 | r22918 | |
| 764 | 727 | { |
| 765 | 728 | m_blitter.colpos = m_blitter.scb + (m_suzy.data[COLLOFFL] | (m_suzy.data[COLLOFFH]<<8)); |
| 766 | 729 | m_blitter.mode = m_blitter.spr_ctl0 & 0x07; |
| 767 | | |
| 768 | | if (m_blitter.spr_ctl1 & 0x80) // totally literal sprite |
| 769 | | m_blitter.line_function = blit_line[m_blitter.spr_ctl0 >> 6]; |
| 770 | | else |
| 771 | | m_blitter.line_function = blit_rle_line[m_blitter.spr_ctl0 >> 6]; |
| 772 | | |
| 730 | m_blitter.use_rle = m_blitter.spr_ctl1 & 0x80 ? 0 : 1; |
| 731 | m_blitter.line_color = (m_blitter.spr_ctl0 >> 6) & 0x03; |
| 732 | |
| 773 | 733 | m_blitter.sprite_collide = !(m_blitter.spr_coll & 0x20); |
| 774 | 734 | m_blitter.spritenr = m_blitter.spr_coll & 0x0f; |
| 775 | 735 | m_blitter.fred = 0; |
| r22917 | r22918 | |
| 1395 | 1355 | |
| 1396 | 1356 | void lynx_state::lynx_timer_init(int which) |
| 1397 | 1357 | { |
| 1398 | | memset( &m_timer[which], 0, sizeof(LYNX_TIMER) ); |
| 1358 | memset(&m_timer[which], 0, sizeof(LYNX_TIMER)); |
| 1399 | 1359 | m_timer[which].timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(lynx_state::lynx_timer_shot),this)); |
| 1360 | |
| 1361 | state_save_register_item(machine(), "Lynx", NULL, which, m_timer[which].bakup); |
| 1362 | state_save_register_item(machine(), "Lynx", NULL, which, m_timer[which].cntrl1); |
| 1363 | state_save_register_item(machine(), "Lynx", NULL, which, m_timer[which].cntrl2); |
| 1364 | state_save_register_item(machine(), "Lynx", NULL, which, m_timer[which].counter); |
| 1365 | state_save_register_item(machine(), "Lynx", NULL, which, m_timer[which].timer_active); |
| 1400 | 1366 | } |
| 1401 | 1367 | |
| 1402 | 1368 | void lynx_state::lynx_timer_signal_irq(int which) |
| 1403 | 1369 | { |
| 1404 | | if ( ( m_timer[which].cntrl1 & 0x80 ) && ( which != 4 ) ) // if interrupts are enabled and timer != 4 |
| 1370 | if ((m_timer[which].cntrl1 & 0x80) && (which != 4)) // if interrupts are enabled and timer != 4 |
| 1405 | 1371 | { |
| 1406 | | m_mikey.data[0x81] |= ( 1 << which ); // set interupt poll register |
| 1372 | m_mikey.data[0x81] |= (1 << which); // set interupt poll register |
| 1407 | 1373 | m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE); |
| 1408 | 1374 | m_maincpu->set_input_line(M65SC02_IRQ_LINE, ASSERT_LINE); |
| 1409 | 1375 | } |
| 1410 | | switch ( which ) // count down linked timers |
| 1376 | switch (which) // count down linked timers |
| 1411 | 1377 | { |
| 1412 | 1378 | case 0: |
| 1413 | 1379 | switch (m_timer[2].counter) |
| r22917 | r22918 | |
| 1427 | 1393 | default: |
| 1428 | 1394 | lynx_draw_line(); |
| 1429 | 1395 | } |
| 1430 | | lynx_timer_count_down( 2 ); |
| 1396 | lynx_timer_count_down(2); |
| 1431 | 1397 | break; |
| 1432 | 1398 | case 2: |
| 1433 | 1399 | copybitmap(m_bitmap, m_bitmap_temp, 0, 0, 0, 0, machine().primary_screen->cliprect()); |
| 1434 | | lynx_timer_count_down( 4 ); |
| 1400 | lynx_timer_count_down(4); |
| 1435 | 1401 | break; |
| 1436 | 1402 | case 1: |
| 1437 | | lynx_timer_count_down( 3 ); |
| 1403 | lynx_timer_count_down(3); |
| 1438 | 1404 | break; |
| 1439 | 1405 | case 3: |
| 1440 | | lynx_timer_count_down( 5 ); |
| 1406 | lynx_timer_count_down(5); |
| 1441 | 1407 | break; |
| 1442 | 1408 | case 5: |
| 1443 | | lynx_timer_count_down( 7 ); |
| 1409 | lynx_timer_count_down(7); |
| 1444 | 1410 | break; |
| 1445 | 1411 | case 7: |
| 1446 | | lynx_audio_count_down( m_audio, 0 ); |
| 1412 | m_sound->count_down(0); |
| 1447 | 1413 | break; |
| 1448 | 1414 | } |
| 1449 | 1415 | } |
| 1450 | 1416 | |
| 1451 | 1417 | void lynx_state::lynx_timer_count_down(int which) |
| 1452 | 1418 | { |
| 1453 | | if ( ( m_timer[which].cntrl1 & 0x0f ) == 0x0f ) // count and linking enabled |
| 1419 | if ((m_timer[which].cntrl1 & 0x0f) == 0x0f) // count and linking enabled |
| 1454 | 1420 | { |
| 1455 | | if ( m_timer[which].counter > 0 ) |
| 1421 | if (m_timer[which].counter > 0) |
| 1456 | 1422 | { |
| 1457 | 1423 | m_timer[which].counter--; |
| 1458 | 1424 | //m_timer[which].borrow_in = 1; |
| 1459 | 1425 | return; |
| 1460 | 1426 | } |
| 1461 | | if ( m_timer[which].counter == 0 ) |
| 1427 | if (m_timer[which].counter == 0) |
| 1462 | 1428 | { |
| 1463 | 1429 | if (m_timer[which].cntrl2 & 0x01) // borrow out |
| 1464 | 1430 | { |
| 1465 | 1431 | lynx_timer_signal_irq(which); |
| 1466 | | if ( m_timer[which].cntrl1 & 0x10 ) // if reload enabled |
| 1432 | if (m_timer[which].cntrl1 & 0x10) // if reload enabled |
| 1467 | 1433 | { |
| 1468 | 1434 | m_timer[which].counter = m_timer[which].bakup; |
| 1469 | 1435 | } |
| r22917 | r22918 | |
| 1502 | 1468 | |
| 1503 | 1469 | TIMER_CALLBACK_MEMBER(lynx_state::lynx_timer_shot) |
| 1504 | 1470 | { |
| 1505 | | lynx_timer_signal_irq( param ); |
| 1506 | | if ( ! ( m_timer[param].cntrl1 & 0x10 ) ) // if reload not enabled |
| 1471 | lynx_timer_signal_irq(param); |
| 1472 | if (!(m_timer[param].cntrl1 & 0x10)) // if reload not enabled |
| 1507 | 1473 | { |
| 1508 | 1474 | m_timer[param].timer_active = 0; |
| 1509 | 1475 | m_timer[param].cntrl2 |= 8; // set timer done |
| r22917 | r22918 | |
| 1722 | 1688 | case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: |
| 1723 | 1689 | case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f: |
| 1724 | 1690 | case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x50: |
| 1725 | | value = lynx_audio_read(m_audio, offset); |
| 1691 | value = m_sound->read(space, offset); |
| 1726 | 1692 | break; |
| 1727 | 1693 | |
| 1728 | 1694 | case 0x80: |
| r22917 | r22918 | |
| 1789 | 1755 | case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: |
| 1790 | 1756 | case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f: |
| 1791 | 1757 | case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x50: |
| 1792 | | lynx_audio_write(m_audio, offset, data); |
| 1758 | m_sound->write(space, offset, data); |
| 1793 | 1759 | return; |
| 1794 | 1760 | |
| 1795 | 1761 | case 0x80: |
| r22917 | r22918 | |
| 1895 | 1861 | * when these are safe in the cpu */ |
| 1896 | 1862 | m_memory_config = data; |
| 1897 | 1863 | |
| 1898 | | if (data & 1) { |
| 1864 | if (data & 1) |
| 1865 | { |
| 1899 | 1866 | space.install_readwrite_bank(0xfc00, 0xfcff, "bank1"); |
| 1900 | | } else { |
| 1867 | membank("bank1")->set_base(m_mem_fc00); |
| 1868 | } |
| 1869 | else |
| 1901 | 1870 | space.install_readwrite_handler(0xfc00, 0xfcff, read8_delegate(FUNC(lynx_state::suzy_read),this), write8_delegate(FUNC(lynx_state::suzy_write),this)); |
| 1871 | |
| 1872 | if (data & 2) |
| 1873 | { |
| 1874 | space.install_readwrite_bank(0xfd00, 0xfdff, "bank2"); |
| 1875 | membank("bank2")->set_base(m_mem_fd00); |
| 1902 | 1876 | } |
| 1903 | | if (data & 2) { |
| 1904 | | space.install_readwrite_bank(0xfd00, 0xfdff, "bank2"); |
| 1905 | | } else { |
| 1877 | else |
| 1906 | 1878 | space.install_readwrite_handler(0xfd00, 0xfdff, read8_delegate(FUNC(lynx_state::mikey_read),this), write8_delegate(FUNC(lynx_state::mikey_write),this)); |
| 1907 | | } |
| 1908 | 1879 | |
| 1909 | | if (data & 1) |
| 1910 | | membank("bank1")->set_base(m_mem_fc00); |
| 1911 | | if (data & 2) |
| 1912 | | membank("bank2")->set_base(m_mem_fd00); |
| 1913 | 1880 | membank("bank3")->set_entry((data & 4) ? 1 : 0); |
| 1914 | 1881 | membank("bank4")->set_entry((data & 8) ? 1 : 0); |
| 1915 | 1882 | } |
| r22917 | r22918 | |
| 1958 | 1925 | { |
| 1959 | 1926 | m_bitmap_temp.allocate(160,102,0,0); |
| 1960 | 1927 | |
| 1961 | | int i; |
| 1928 | // save driver variables |
| 1962 | 1929 | save_item(NAME(m_memory_config)); |
| 1963 | | save_pointer(NAME(m_mem_fe00.target()), m_mem_fe00.bytes()); |
| 1930 | save_item(NAME(m_sign_AB)); |
| 1931 | save_item(NAME(m_sign_CD)); |
| 1932 | save_item(NAME(m_palette)); |
| 1933 | save_item(NAME(m_rotate)); |
| 1934 | // save blitter variables |
| 1935 | save_item(NAME(m_blitter.screen)); |
| 1936 | save_item(NAME(m_blitter.colbuf)); |
| 1937 | save_item(NAME(m_blitter.colpos)); |
| 1938 | save_item(NAME(m_blitter.xoff)); |
| 1939 | save_item(NAME(m_blitter.yoff)); |
| 1940 | save_item(NAME(m_blitter.mode)); |
| 1941 | save_item(NAME(m_blitter.spr_coll)); |
| 1942 | save_item(NAME(m_blitter.spritenr)); |
| 1943 | save_item(NAME(m_blitter.x_pos)); |
| 1944 | save_item(NAME(m_blitter.y_pos)); |
| 1945 | save_item(NAME(m_blitter.width)); |
| 1946 | save_item(NAME(m_blitter.height)); |
| 1947 | save_item(NAME(m_blitter.tilt_accumulator)); |
| 1948 | save_item(NAME(m_blitter.width_accumulator)); |
| 1949 | save_item(NAME(m_blitter.height_accumulator)); |
| 1950 | save_item(NAME(m_blitter.width_offset)); |
| 1951 | save_item(NAME(m_blitter.height_offset)); |
| 1952 | save_item(NAME(m_blitter.stretch)); |
| 1953 | save_item(NAME(m_blitter.tilt)); |
| 1954 | save_item(NAME(m_blitter.color)); |
| 1955 | save_item(NAME(m_blitter.bitmap)); |
| 1956 | save_item(NAME(m_blitter.use_rle)); |
| 1957 | save_item(NAME(m_blitter.line_color)); |
| 1958 | save_item(NAME(m_blitter.spr_ctl0)); |
| 1959 | save_item(NAME(m_blitter.spr_ctl1)); |
| 1960 | save_item(NAME(m_blitter.scb)); |
| 1961 | save_item(NAME(m_blitter.scb_next)); |
| 1962 | save_item(NAME(m_blitter.sprite_collide)); |
| 1963 | save_item(NAME(m_blitter.everon)); |
| 1964 | save_item(NAME(m_blitter.fred)); |
| 1965 | save_item(NAME(m_blitter.memory_accesses)); |
| 1966 | save_item(NAME(m_blitter.no_collide)); |
| 1967 | save_item(NAME(m_blitter.vstretch)); |
| 1968 | save_item(NAME(m_blitter.lefthanded)); |
| 1969 | save_item(NAME(m_blitter.busy)); |
| 1970 | // save suzy variables |
| 1971 | save_item(NAME(m_suzy.data)); |
| 1972 | save_item(NAME(m_suzy.high)); |
| 1973 | save_item(NAME(m_suzy.low)); |
| 1974 | save_item(NAME(m_suzy.signed_math)); |
| 1975 | save_item(NAME(m_suzy.accumulate)); |
| 1976 | save_item(NAME(m_suzy.accumulate_overflow)); |
| 1977 | // save mikey variables |
| 1978 | save_item(NAME(m_mikey.data)); |
| 1979 | save_item(NAME(m_mikey.disp_addr)); |
| 1980 | save_item(NAME(m_mikey.vb_rest)); |
| 1981 | // save uart variables |
| 1982 | save_item(NAME(m_uart.serctl)); |
| 1983 | save_item(NAME(m_uart.data_received)); |
| 1984 | save_item(NAME(m_uart.data_to_send)); |
| 1985 | save_item(NAME(m_uart.buffer)); |
| 1986 | save_item(NAME(m_uart.received)); |
| 1987 | save_item(NAME(m_uart.sending)); |
| 1988 | save_item(NAME(m_uart.buffer_loaded)); |
| 1989 | |
| 1964 | 1990 | machine().save().register_postload(save_prepost_delegate(FUNC(lynx_state::lynx_postload), this)); |
| 1965 | 1991 | |
| 1966 | 1992 | membank("bank3")->configure_entry(0, memregion("maincpu")->base() + 0x0000); |
| r22917 | r22918 | |
| 1968 | 1994 | membank("bank4")->configure_entry(0, memregion("maincpu")->base() + 0x01fa); |
| 1969 | 1995 | membank("bank4")->configure_entry(1, m_mem_fffa); |
| 1970 | 1996 | |
| 1971 | | m_audio = machine().device("custom"); |
| 1972 | | |
| 1973 | | memset(&m_suzy, 0, sizeof(m_suzy)); |
| 1974 | | |
| 1975 | | for (i = 0; i < NR_LYNX_TIMERS; i++) |
| 1997 | for (int i = 0; i < NR_LYNX_TIMERS; i++) |
| 1976 | 1998 | lynx_timer_init(i); |
| 1977 | 1999 | } |
| 1978 | 2000 | |