@@ -118,54 +118,95 @@ namespace m5
118118
119119 std::uint32_t RX8130_Class::setTimerIRQ (std::uint32_t msec)
120120 {
121- // タイマー周期の除数
122- uint32_t div = 1 ;
123- // タイマー周期の乗数
124- uint32_t mul = 1 ;
121+ // Source clocks in the order they are tried. period = mul_ms / div [ms].
122+ // max_ms = 65535 * period, so msec <= max_ms keeps msec * div within uint32.
123+ // max_cnt = min(65535, 0xFFFFFFFF / mul_ms), so cnt * mul_ms (the period) stays within uint32.
124+ struct clk_t { std::uint32_t mul_ms; std::uint32_t div; std::uint32_t max_ms; std::uint16_t max_cnt; std::uint8_t tsel; };
125+ static constexpr clk_t clks[] = {
126+ { 1000 , 64 , 1023984 , 65535 , 0x01 }, // 64 Hz
127+ { 1000 , 1 , 65535000 , 65535 , 0x02 }, // 1 Hz
128+ { 60000 , 1 , 3932100000 , 65535 , 0x03 }, // 1/60 Hz
129+ { 3600000 , 1 , 0xFFFFFFFF , 1193 , 0x04 }, // 1/3600 Hz
130+ { 1000 , 4096 , 15999 , 65535 , 0x00 }, // 4096 Hz (last resort: its /IRQ pulse is only 122us)
131+ };
132+ static constexpr std::size_t NCLK = sizeof (clks) / sizeof (clks[0 ]);
133+ // The /IRQ pulse auto-releases after 122us with the 4096Hz clock but 7.57ms with the others,
134+ // so take the finest non-4096Hz clock whose rounded count keeps the period error under 1/256
135+ // and has >= MIN_COUNT counts (the first countdown can be short by up to one source clock,
136+ // 1s for the 1/60Hz and 1/3600Hz clocks, so this bounds that to ~6% or less).
137+ static constexpr std::uint32_t MIN_COUNT = 16 ;
125138
126- uint8_t tsel_bits = 0 ;
127- if (msec < 65536 * 1000 / 4096 ) { // 約16秒
128- tsel_bits = 0x00 ;
129- div = 4096 ;
130- } else if (msec < 65536 * 1000 / 64 ) { // 約1024秒(約17分)
131- tsel_bits = 0x01 ;
132- div = 64 ;
133- } else if (msec < 65536 * 1000 ) { // 約65535秒(約18時間)
134- tsel_bits = 0x02 ;
135- } else if (msec < 65536 * 60 ) { // 約3,932,160秒(約45日)
136- mul = 60 ;
137- tsel_bits = 0x03 ;
138- } else { // msec < 65536*3600 // 約39,321,600秒(約1年3ヶ月)
139- mul = 3600 ;
140- tsel_bits = 0x04 ;
139+ std::uint32_t cycle = 0 ;
140+ const clk_t * sel = nullptr ;
141+ if (msec != 0 ) {
142+ bool overflowed = false ; // a finer clock ran out of range: round up so the period never steps back
143+ for (std::size_t i = 0 ; i < NCLK ; ++i) {
144+ const clk_t & c = clks[i];
145+ if (msec > c.max_ms ) { overflowed = true ; continue ; }
146+ // Everything below is in units of msec * div: cnt counts of mul_ms each, err the remainder.
147+ std::uint32_t num = msec * c.div ;
148+ std::uint32_t cnt = num / c.mul_ms ;
149+ std::uint32_t err = num % c.mul_ms ;
150+ if (overflowed ? (err != 0 ) : (err * 2 >= c.mul_ms )) { ++cnt; err = c.mul_ms - err; }
151+ if (cnt > c.max_cnt ) { cnt = c.max_cnt ; err = num - cnt * c.mul_ms ; }
152+ // Accept when the error is within 1/256 (~0.39%) of the request (err < mul_ms, so no overflow).
153+ if (i + 1 == NCLK || (cnt >= MIN_COUNT && (err << 8 ) <= num)) {
154+ sel = &c; cycle = cnt; break ;
155+ }
156+ }
157+ if (sel == nullptr ) { return 0 ; } // unreachable (1/3600Hz covers all of uint32); fail safe = stay stopped
141158 }
142159
143- std::uint32_t result = 0 ;
144- std::uint8_t regdata[3 ];
145- if (readRegister (0x1A , regdata, 3 )) {
146- mul *= 1000 ;
147- uint32_t cycle = (msec * div + (mul >> 1 )) / mul;
148- if (cycle > 65535 ) { cycle = 65535 ; }
149- result = cycle * mul / div;
150-
151- regdata[0 ] = cycle & 0xff ;
152- regdata[1 ] = (cycle >> 8 ) & 0xff ;
153- if (cycle > 0 ) {
154- // Clear timer select bits & TE flag
155- std::uint8_t reg0x1C = regdata[2 ] & ~0x17 ;
156- // Set timer select bits & TE flag
157- reg0x1C |= 0x10 | tsel_bits;
158- regdata[2 ] = reg0x1C;
159- bitOn (0x1E , 0x10 );
160- } else {
161- // Clear TE flag
162- regdata[2 ] &= ~0x10 ;
163- bitOff (0x1E , 0x10 );
160+ // Sequence per datasheet Figure 48: TE=0 (+TSEL) -> clear TF -> TIE -> preset -> TE=1 last,
161+ // so the first event cannot precede TIE. On any I2C failure the timer is stopped (verified by
162+ // read-back where the bus allows it) and 0 is returned; the caller cannot tell that from a
163+ // requested stop, and if even the stop fails the hardware state is unknown.
164+ // 0x1D flags are write-0-to-clear (writing 1 is ignored, VBFF is read-only), so TF is cleared
165+ // with a single write that leaves the other flags untouched (a read-modify-write would drop
166+ // a flag raised in between).
167+ static constexpr std::uint8_t FLAG_CLEAR_TF = 0xAF ;
168+ auto stop_timer = [this ](void ) -> bool {
169+ for (int retry = 0 ; retry < 3 ; ++retry) {
170+ std::uint8_t ext = 0 , ctl = 0 ;
171+ if (bitOff (0x1C , 0x10 ) && bitOff (0x1E , 0x10 )
172+ && readRegister (0x1C , &ext, 1 ) && readRegister (0x1E , &ctl, 1 )
173+ && !(ext & 0x10 ) && !(ctl & 0x10 )) { return true ; }
164174 }
165- writeRegister (0x1A , regdata, 3 );
175+ return false ;
176+ };
177+ std::uint8_t reg0x1C = 0 ;
178+ if (cycle == 0 ) {
179+ stop_timer ();
180+ writeRegister8 (0x1D , FLAG_CLEAR_TF );
181+ return 0 ;
166182 }
167-
168- return result;
183+ bool ok = readRegister (0x1C , ®0x1C, 1 );
184+ if (ok) {
185+ reg0x1C = (reg0x1C & ~0x17 ) | sel->tsel ;
186+ ok = writeRegister8 (0x1C , reg0x1C)
187+ && writeRegister8 (0x1D , FLAG_CLEAR_TF )
188+ && bitOn (0x1E , 0x10 );
189+ }
190+ if (ok) {
191+ // While TE=0 the counter registers read back the preset, so verify the write took
192+ // (a corrupted preset was observed on a shared bus) and retry a few times.
193+ std::uint8_t regdata[2 ] = { (std::uint8_t )(cycle & 0xff ), (std::uint8_t )((cycle >> 8 ) & 0xff ) };
194+ ok = false ;
195+ for (int retry = 0 ; retry < 3 && !ok; ++retry) {
196+ std::uint8_t verify[2 ] = { 0 , 0 };
197+ ok = writeRegister (0x1A , regdata, 2 )
198+ && readRegister (0x1A , verify, 2 )
199+ && verify[0 ] == regdata[0 ] && verify[1 ] == regdata[1 ];
200+ }
201+ }
202+ if (ok) { ok = writeRegister8 (0x1C , reg0x1C | 0x10 ); }
203+ if (!ok) {
204+ stop_timer ();
205+ return 0 ;
206+ }
207+ // Actual period rounded to the nearest ms (cycle * mul_ms fits by max_cnt); never 0 while running.
208+ std::uint32_t result = (cycle * sel->mul_ms + (sel->div >> 1 )) / sel->div ;
209+ return result ? result : 1 ;
169210 }
170211
171212 int RX8130_Class::setAlarmIRQ (const rtc_date_t * date, const rtc_time_t * time)
@@ -239,15 +280,15 @@ namespace m5
239280 void RX8130_Class::clearIRQ (void )
240281 {
241282 if (isEnabled ()) {
242- bitOff (0x1D , 0x18 );
283+ writeRegister8 (0x1D , 0xA7 ); // W0C: clear TF and AF only
243284 }
244285 }
245286
246287 void RX8130_Class::disableIRQ (void )
247288 {
248289 if (isEnabled ()) {
249290 bitOff (0x1E , 0x18 );
250- bitOff (0x1D , 0x18 );
291+ writeRegister8 (0x1D , 0xA7 ); // W0C: clear TF and AF only
251292 }
252293 }
253294
0 commit comments