video: keep the beam step per clock count instead of dividing each time 12673a41ba17
video_step() runs after every instruction and worked out how far the
beam moves, PIXEL_FREQ * steps / mhz, twice, in double precision, with
two divisions. The clock count is almost always a handful, so the
quotient for each count below 32 is now kept in a table filled with the
same expression whenever mhz changes, and the two counters share one
result. The same expression gives the same double, so the beam position
is bit-for-bit what it was; larger counts (after a HostFS call) still
use the formula.
Measured as before (CPU seconds per emulated second, median of 3):
narvi idle 2.032 -> 2.046 (49%, within run-to-run spread)
narvi busy 1.884 -> 1.846 (53% -> 54% of a real X16)
x86 was within this machine's noise. The determinism check matches on
x86 and riscv64.
- Author
- Andy McCall @andymccall <[email protected]>
- via
- agent
agent:claude@andymccall <[email protected]> · keyless, under your grant for changes,refs,seal · key 3d452845da48
- When
- · 2026-10-21 01:41 UTC
- Signature
- ◈ sealed by agent:claude@andymccall
- Hash
12673a41ba171e8324048662eae7ffd77c14453bb43edca023026f60f34d4978- Depends on
- 03a3459e6372
- Depended on by
- 8e8d9cad1884
- In bookmarks
- main riscv-1
- Edits
- 4 atoms, 0 tree operations, 1 file
Browse the tree at this change
modified src/video.c +27 −2
| … | 1236 unchanged lines |
||
| 1237 | 1237 | } |
|
| 1238 | 1238 | } |
|
| 1239 | 1239 | ||
| 1240 | + | // How far the beam moves in `steps` CPU clocks: PIXEL_FREQ * steps / mhz. |
|
| 1241 | + | // video_step() runs after every instruction, almost always for a handful |
|
| 1242 | + | // of clocks, so the quotient for each small count is kept rather than |
|
| 1243 | + | // divided out again; it is the same expression, so the same value. |
|
| 1244 | + | static double |
|
| 1245 | + | scan_pixels(float mhz, float steps) |
|
| 1246 | + | { |
|
| 1247 | + | static float table_mhz; |
|
| 1248 | + | static double table[32]; |
|
| 1249 | + | if (mhz != table_mhz) { |
|
| 1250 | + | for (int i = 0; i < 32; i++) { |
|
| 1251 | + | table[i] = PIXEL_FREQ * (float)i / mhz; |
|
| 1252 | + | } |
|
| 1253 | + | table_mhz = mhz; |
|
| 1254 | + | } |
|
| 1255 | + | if (steps >= 0 && steps < 32) { |
|
| 1256 | + | uint32_t s = (uint32_t)steps; |
|
| 1257 | + | if (s == steps) { |
|
| 1258 | + | return table[s]; |
|
| 1259 | + | } |
|
| 1260 | + | } |
|
| 1261 | + | return PIXEL_FREQ * steps / mhz; |
|
| 1262 | + | } |
|
| 1263 | + | ||
| 1240 | 1264 | bool |
|
| 1241 | 1265 | video_step(float mhz, float steps, bool midline) |
|
| 1242 | 1266 | { |
|
| 1243 | 1267 | uint16_t y = 0; |
|
| 1244 | 1268 | bool ntsc_mode = reg_composer[0] & 2; |
|
| 1245 | 1269 | bool new_frame = false; |
|
| 1246 | - | vga_scan_pos_x += PIXEL_FREQ * steps / mhz; |
|
| 1270 | + | const double pixels = scan_pixels(mhz, steps); |
|
| 1271 | + | vga_scan_pos_x += pixels; |
|
| 1247 | 1272 | if (vga_scan_pos_x > VGA_SCAN_WIDTH) { |
|
| 1248 | 1273 | vga_scan_pos_x -= VGA_SCAN_WIDTH; |
|
| 1249 | 1274 | if (!ntsc_mode) { |
|
| … | 15 unchanged lines |
||
| 1265 | 1290 | render_line(vga_scan_pos_y - VGA_Y_OFFSET, vga_scan_pos_x); |
|
| 1266 | 1291 | } |
|
| 1267 | 1292 | } |
|
| 1268 | - | ntsc_half_cnt += PIXEL_FREQ * steps / mhz; |
|
| 1293 | + | ntsc_half_cnt += pixels; |
|
| 1269 | 1294 | if (ntsc_half_cnt > NTSC_HALF_SCAN_WIDTH) { |
|
| 1270 | 1295 | ntsc_half_cnt -= NTSC_HALF_SCAN_WIDTH; |
|
| 1271 | 1296 | if (ntsc_mode) { |
|
| … | 1186 unchanged lines |