@@ -10,9 +10,7 @@ E0C6S48: 8192x12 ROM, 768x4 RAM, 2*102x4 VRAM, LCD has 16 commons and 51 segment
10
10
TODO:
11
11
- finish i/o ports
12
12
- serial interface
13
- - buzzer envelope addition
14
13
- what happens if OSC3 is selected while OSCC (bit 2) is low?
15
- - K input interrupt can trigger if input is active while writing to the mask register
16
14
- add mask options for ports (eg. buzzer on output port R4x is optional)
17
15
18
16
*/
@@ -148,6 +146,7 @@ void e0c6s46_device::device_start()
148
146
m_bz_43_on = 0 ;
149
147
m_bz_freq = 0 ;
150
148
m_bz_envelope = 0 ;
149
+ m_bz_envelope_count = 0 ;
151
150
m_bz_duty_ratio = 0 ;
152
151
m_bz_1shot_on = 0 ;
153
152
m_bz_1shot_running = false ;
@@ -190,6 +189,7 @@ void e0c6s46_device::device_start()
190
189
save_item (NAME (m_bz_43_on));
191
190
save_item (NAME (m_bz_freq));
192
191
save_item (NAME (m_bz_envelope));
192
+ save_item (NAME (m_bz_envelope_count));
193
193
save_item (NAME (m_bz_duty_ratio));
194
194
save_item (NAME (m_bz_1shot_on));
195
195
save_item (NAME (m_bz_1shot_running));
@@ -211,12 +211,12 @@ void e0c6s46_device::device_reset()
211
211
memset (m_irqflag, 0 , sizeof (m_irqflag));
212
212
memset (m_irqmask, 0 , sizeof (m_irqmask));
213
213
214
- // reset other i/o
214
+ // reset other I/O
215
215
m_data->write_byte (0xf41 , 0xf );
216
216
m_data->write_byte (0xf54 , 0xf );
217
217
m_data->write_byte (0xf70 , 0x0 );
218
218
m_data->write_byte (0xf71 , 0x8 );
219
- m_data->write_byte (0xf73 , m_svd & 0xc0 );
219
+ m_data->write_byte (0xf73 , m_svd & 3 );
220
220
221
221
m_data->write_byte (0xf74 , 0x0 );
222
222
m_data->write_byte (0xf75 , 0x4 );
@@ -276,13 +276,19 @@ bool e0c6s46_device::check_interrupt()
276
276
for (int pri = 5 ; pri >= 0 ; pri--)
277
277
{
278
278
// hw glitch note, not emulated: if a new interrupt is requested in the
279
- // middle of handling this interrupt, irq vector may be an OR of 2 vectors
280
- m_irq_vector = 2 * pri + 2 ;
279
+ // middle of handling this interrupt, IRQ vector may be an OR of 2 vectors
280
+ m_irq_vector = 2 * pri + 2 ;
281
281
int reg = priorder[pri];
282
282
m_irq_id = reg;
283
283
284
284
switch (reg)
285
285
{
286
+ // IK0/IK1 only have 1 flag bit
287
+ case IRQREG_INPUT0: case IRQREG_INPUT1:
288
+ if (m_irqflag[reg])
289
+ return true ;
290
+ break ;
291
+
286
292
// other: mask vs flag
287
293
default :
288
294
if (m_irqflag[reg] & m_irqmask[reg])
@@ -301,20 +307,24 @@ void e0c6s46_device::execute_set_input(int line, int state)
301
307
return ;
302
308
303
309
state = (state) ? 1 : 0 ;
304
- int port = line >> 2 & 1 ;
310
+ u8 port = line >> 2 & 1 ;
305
311
u8 bit = 1 << (line & 3 );
306
312
307
313
u8 prev = m_port_k[port];
308
314
m_port_k[port] = (m_port_k[port] & ~bit) | (state ? bit : 0 );
309
315
310
- // set interrupt on falling/rising edge of input
316
+ // set IRQ flag on falling/rising edge of input
311
317
u8 dfk = (port == 0 ) ? m_dfk0 : 0xf ;
312
318
u8 edge = ~(prev ^ dfk) & (m_port_k[port] ^ dfk) & bit;
313
319
if (m_irqmask[IRQREG_INPUT0 + port] & edge)
314
320
{
315
- m_irqflag[IRQREG_INPUT0 + port] |= edge ;
321
+ m_irqflag[IRQREG_INPUT0 + port] |= 1 ;
316
322
m_possible_irq = true ;
317
323
}
324
+
325
+ // clock programmable timer on falling edge of K03
326
+ if ((prev & ~m_port_k[port] & bit) && m_prgtimer_on && (m_prgtimer_select & 7 ) < 2 )
327
+ clock_prgtimer ();
318
328
}
319
329
320
330
@@ -330,28 +340,22 @@ void e0c6s46_device::write_r(u8 port, u8 data)
330
340
data &= 0xf ;
331
341
m_port_r[port] = data;
332
342
333
- // ports R0x-R3x can be high-impedance
334
- u8 out = data;
335
- if (port < 4 && !(m_r_dir >> port & 1 ))
336
- out = 0xf ;
337
-
338
- switch (port)
343
+ if (port < 4 )
339
344
{
340
- case 0 : m_write_r[0 ](port, out, 0xff ); break ;
341
- case 1 : m_write_r[1 ](port, out, 0xff ); break ;
342
- case 2 : m_write_r[2 ](port, out, 0xff ); break ;
343
- case 3 : m_write_r[3 ](port, out, 0xff ); break ; // TODO: R33 PTCLK/_SRDY
344
-
345
- // R4x: special output
346
- case 4 :
347
- // d3: buzzer on: direct output or 1-shot output
348
- if ((data & 8 ) != m_bz_43_on)
349
- {
350
- m_bz_43_on = data & 8 ;
351
- reset_buzzer ();
352
- }
353
- write_r4_out ();
354
- break ;
345
+ // ports R0-R3 can be high-impedance
346
+ u8 mask = BIT (m_r_dir, port) ? 0xf : 0 ;
347
+ m_write_r[port](port, data | (mask ^ 0xf ), mask);
348
+ }
349
+ else
350
+ {
351
+ // R43: buzzer on: direct output or 1-shot output
352
+ if ((data & 8 ) != m_bz_43_on)
353
+ {
354
+ m_bz_43_on = data & 8 ;
355
+ reset_bz_envelope ();
356
+ reset_buzzer ();
357
+ }
358
+ write_r4_out ();
355
359
}
356
360
}
357
361
@@ -360,8 +364,8 @@ void e0c6s46_device::write_r4_out()
360
364
// R40: _FOUT(clock inverted output)
361
365
// R42: FOUT or _BZ
362
366
// R43: BZ(buzzer)
363
- u8 out = (m_port_r[4 ] & 2 ) | (m_bz_pulse << 3 ) | (m_bz_pulse << 2 ^ 4 );
364
- m_write_r[4 ](4 , out, 0xff );
367
+ u8 data = (m_port_r[4 ] & 2 ) | (m_bz_pulse << 3 ) | (m_bz_pulse << 2 ^ 4 );
368
+ m_write_r[4 ](4 , data, 0xf );
365
369
}
366
370
367
371
@@ -372,20 +376,18 @@ void e0c6s46_device::write_p(u8 port, u8 data)
372
376
data &= 0xf ;
373
377
m_port_p[port] = data;
374
378
375
- // don't output if port direction is set to input
376
- if (!(m_p_dir >> port & 1 ))
377
- return ;
378
-
379
- m_write_p[port](port, data, 0xff );
379
+ // high-impedance if port is set to input
380
+ u8 mask = BIT (m_p_dir, port) ? 0xf : 0 ;
381
+ m_write_p[port](port, data | (mask ^ 0xf ), mask);
380
382
}
381
383
382
384
u8 e0c6s46_device::read_p (u8 port)
383
385
{
384
386
// return written value if port direction is set to output
385
- if (m_p_dir >> port & 1 )
387
+ if (BIT ( m_p_dir, port) )
386
388
return m_port_p[port];
387
389
388
- return m_read_p[port](port, 0xff );
390
+ return m_read_p[port](port);
389
391
}
390
392
391
393
@@ -410,6 +412,10 @@ TIMER_CALLBACK_MEMBER(e0c6s46_device::core_256_cb)
410
412
if (m_bz_1shot_on != 0 && m_256_src_pulse == 1 )
411
413
clock_bz_1shot ();
412
414
415
+ // clock buzzer envelope on rising edge if it's on
416
+ if (m_bz_envelope & 1 && m_256_src_pulse == 1 )
417
+ clock_bz_envelope ();
418
+
413
419
// clock-timer is always running, advance it on falling edge
414
420
// (handle clock_clktimer last in case of watchdog reset)
415
421
if (m_256_src_pulse == 0 )
@@ -440,7 +446,7 @@ void e0c6s46_device::clock_clktimer()
440
446
{
441
447
m_clktimer_count++;
442
448
443
- // irq on falling edge of 32, 8, 2, 1hz
449
+ // IRQ on falling edge of 32, 8, 2, 1hz
444
450
u8 flag = 0 ;
445
451
if ((m_clktimer_count & 0x07 ) == 0 )
446
452
flag |= 1 ;
@@ -478,18 +484,18 @@ void e0c6s46_device::clock_stopwatch()
478
484
{
479
485
m_swl_slice = 0 ;
480
486
481
- // bcd counter, irq on falling edge of 10 and 1hz
487
+ // bcd counter, IRQ on falling edge of 10 and 1hz
482
488
m_swl_count = (m_swl_count + 1 ) % 10 ;
483
489
if (m_swl_count == 0 )
484
490
{
485
491
m_irqflag[IRQREG_STOPWATCH] |= 1 ;
486
492
m_swh_count = (m_swh_count + 1 ) % 10 ;
487
493
if (m_swh_count == 0 )
488
494
m_irqflag[IRQREG_STOPWATCH] |= 2 ;
489
- }
490
495
491
- if (m_irqflag[IRQREG_STOPWATCH] & m_irqmask[IRQREG_STOPWATCH])
492
- m_possible_irq = true ;
496
+ if (m_irqflag[IRQREG_STOPWATCH] & m_irqmask[IRQREG_STOPWATCH])
497
+ m_possible_irq = true ;
498
+ }
493
499
}
494
500
}
495
501
@@ -498,7 +504,7 @@ void e0c6s46_device::clock_stopwatch()
498
504
499
505
void e0c6s46_device::clock_prgtimer ()
500
506
{
501
- // irq and reload when it reaches zero
507
+ // IRQ and reload when it reaches zero
502
508
if (--m_prgtimer_count == 0 )
503
509
{
504
510
m_irqflag[IRQREG_PRGTIMER] |= 1 ;
@@ -540,7 +546,7 @@ TIMER_CALLBACK_MEMBER(e0c6s46_device::prgtimer_cb)
540
546
void e0c6s46_device::schedule_buzzer ()
541
547
{
542
548
// only schedule next buzzer timeout if it's on
543
- if (m_bz_43_on != 0 && !m_bz_1shot_running)
549
+ if (m_bz_43_on && !m_bz_1shot_running)
544
550
return ;
545
551
546
552
// pulse width differs per frequency selection
@@ -558,10 +564,13 @@ void e0c6s46_device::schedule_buzzer()
558
564
559
565
TIMER_CALLBACK_MEMBER (e0c6s46_device::buzzer_cb)
560
566
{
561
- // invert pulse wave and write to output
562
- m_bz_pulse ^= 1 ;
563
- write_r4_out ();
567
+ // invert pulse wave if buzzer is on
568
+ if (m_bz_43_on && !m_bz_1shot_running)
569
+ m_bz_pulse = 0 ;
570
+ else
571
+ m_bz_pulse ^= 1 ;
564
572
573
+ write_r4_out ();
565
574
schedule_buzzer ();
566
575
}
567
576
@@ -579,8 +588,8 @@ void e0c6s46_device::clock_bz_1shot()
579
588
// reload counter the 1st time
580
589
if (m_bz_1shot_count == 0 )
581
590
{
582
- reset_buzzer ();
583
591
m_bz_1shot_count = (m_bz_freq & 8 ) ? 16 : 8 ;
592
+ reset_buzzer ();
584
593
}
585
594
586
595
// stop ringing when counter reaches 0
@@ -591,6 +600,24 @@ void e0c6s46_device::clock_bz_1shot()
591
600
}
592
601
}
593
602
603
+ void e0c6s46_device::clock_bz_envelope ()
604
+ {
605
+ if (--m_bz_envelope_count == 0 )
606
+ {
607
+ m_bz_envelope_count = (m_bz_envelope & 2 ) ? 32 : 16 ;
608
+
609
+ // maximum duty addition is 7
610
+ if (m_bz_duty_ratio < 7 )
611
+ m_bz_duty_ratio++;
612
+ }
613
+ }
614
+
615
+ void e0c6s46_device::reset_bz_envelope ()
616
+ {
617
+ m_bz_envelope_count = (m_bz_envelope & 2 ) ? 32 : 16 ;
618
+ m_bz_duty_ratio = 0 ;
619
+ }
620
+
594
621
595
622
596
623
// -------------------------------------------------
@@ -667,17 +694,17 @@ u8 e0c6s46_device::io_r(offs_t offset)
667
694
{
668
695
switch (offset)
669
696
{
670
- // irq flags, masks
697
+ // IRQ flags, masks
671
698
case 0x00 : case 0x01 : case 0x02 : case 0x03 : case 0x04 : case 0x05 :
672
699
{
673
- // irq flags are reset(acked) when read
700
+ // IRQ flags are reset(acked) when read
674
701
u8 flag = m_irqflag[offset];
675
702
if (!machine ().side_effects_disabled ())
676
703
m_irqflag[offset] = 0 ;
677
704
return flag;
678
705
}
679
706
case 0x10 : case 0x11 : case 0x12 : case 0x13 : case 0x14 : case 0x15 :
680
- return m_irqmask[offset- 0x10 ];
707
+ return m_irqmask[offset & 7 ];
681
708
682
709
// K input ports
683
710
case 0x40 : case 0x42 :
@@ -761,18 +788,34 @@ void e0c6s46_device::io_w(offs_t offset, u8 data)
761
788
{
762
789
switch (offset)
763
790
{
764
- // irq masks
791
+ // IRQ masks
765
792
case 0x10 : case 0x11 : case 0x12 : case 0x13 : case 0x14 : case 0x15 :
766
793
{
767
794
static const u8 maskmask[6 ] = { 0xf , 3 , 1 , 1 , 0xf , 0xf };
768
- m_irqmask[offset-0x10 ] = data & maskmask[offset-0x10 ];
769
- m_possible_irq = true ;
795
+ u8 prev = m_irqmask[offset & 7 ];
796
+ m_irqmask[offset & 7 ] = data & maskmask[offset & 7 ];
797
+ u8 rise = ~prev & m_irqmask[offset & 7 ];
798
+
799
+ if (rise)
800
+ {
801
+ m_possible_irq = true ;
802
+
803
+ if (offset >= 0x14 )
804
+ {
805
+ u8 port = offset & 1 ;
806
+ u8 dfk = (port == 0 ) ? m_dfk0 : 0xf ;
807
+
808
+ // IK flag gets set if input is active at rising edge of mask bit
809
+ if (rise & (m_port_k[port] ^ dfk))
810
+ m_irqflag[offset & 7 ] |= 1 ;
811
+ }
812
+ }
770
813
break ;
771
814
}
772
815
773
816
// K input ports
774
817
case 0x41 :
775
- // d0-d3: K0x irq on 0: rising edge, 1: falling edge
818
+ // d0-d3: K0x IRQ on 0: rising edge, 1: falling edge
776
819
m_dfk0 = data;
777
820
break ;
778
821
@@ -926,8 +969,8 @@ void e0c6s46_device::io_w(offs_t offset, u8 data)
926
969
// d1: envelope cycle selection
927
970
// d2: reset envelope
928
971
// d3: trigger one-shot buzzer
929
- if (data & 1 )
930
- logerror ( " io_w enabled envelope, PC=$%04X \n " , m_prev_pc );
972
+ if (~m_bz_envelope & data & 1 || data & 4 )
973
+ reset_bz_envelope ( );
931
974
m_bz_envelope = data & 3 ;
932
975
m_bz_1shot_on |= data & 8 ;
933
976
break ;
0 commit comments