@@ -15,6 +15,28 @@ import { loadHex } from './intelhex';
15
15
// ATmega328p params
16
16
const FLASH = 0x8000 ;
17
17
18
+ const timeouts : Function [ ] = new Array < Function > ( ) ;
19
+ const messageName = 'zero-timeout-message' ;
20
+
21
+ function handleMessage ( event : MessageEvent ) {
22
+ if ( event . source == window && event . data == messageName ) {
23
+ event . stopPropagation ( ) ;
24
+ if ( timeouts . length > 0 ) {
25
+ const fn = timeouts . shift ( ) ;
26
+ if ( fn !== undefined ) {
27
+ fn ( ) ;
28
+ }
29
+ }
30
+ }
31
+ }
32
+
33
+ function setZeroTimeout ( fn : Function ) {
34
+ timeouts . push ( fn ) ;
35
+ window . postMessage ( messageName , '*' ) ;
36
+ }
37
+
38
+ window . addEventListener ( 'message' , handleMessage , true ) ;
39
+
18
40
export class AVRRunner {
19
41
readonly program = new Uint16Array ( FLASH ) ;
20
42
readonly cpu : CPU ;
@@ -24,8 +46,10 @@ export class AVRRunner {
24
46
readonly portD : AVRIOPort ;
25
47
readonly usart : AVRUSART ;
26
48
readonly speed = 16e6 ; // 16 MHZ
49
+ readonly workUnitCycles = 100000 ;
27
50
28
51
private stopped = false ;
52
+ private nextTick = 0 ;
29
53
30
54
constructor ( hex : string ) {
31
55
loadHex ( hex , new Uint8Array ( this . program . buffer ) ) ;
@@ -41,13 +65,21 @@ export class AVRRunner {
41
65
this . stopped = false ;
42
66
const workUnitCycles = 500000 ;
43
67
let nextTick = this . cpu . cycles + workUnitCycles ;
68
+
44
69
for ( ; ; ) {
45
70
avrInstruction ( this . cpu ) ;
46
71
this . timer . tick ( ) ;
47
72
this . usart . tick ( ) ;
48
73
if ( this . cpu . cycles >= nextTick ) {
49
74
callback ( this . cpu ) ;
50
- await new Promise ( ( resolve ) => setTimeout ( resolve , 0 ) ) ;
75
+ const prevTime = performance . now ( ) ;
76
+ await new Promise ( ( resolve ) => setZeroTimeout ( resolve ) ) ;
77
+ //await new Promise((resolve) => setTimeout(resolve, 0));
78
+ //await new Promise((resolve) => window.requestAnimationFrame(resolve))
79
+ const afterTime = performance . now ( ) ;
80
+
81
+ console . log ( 'execute await timing' , afterTime - prevTime , 'ms' ) ;
82
+
51
83
if ( this . stopped ) {
52
84
break ;
53
85
}
@@ -56,6 +88,37 @@ export class AVRRunner {
56
88
}
57
89
}
58
90
91
+ // Reimplementatio
92
+ executeNoAsync ( callback : ( cpu : CPU ) => void ) {
93
+ // Initialize on first function start
94
+ // Not sure why this is useful since this.cpu.cycles is always = to 0 on start
95
+ // Maybe for the possibility to stop and resume execution ?
96
+ if ( this . nextTick === 0 ) {
97
+ this . nextTick = this . cpu . cycles + this . workUnitCycles ;
98
+ }
99
+
100
+ for ( ; ; ) {
101
+ avrInstruction ( this . cpu ) ;
102
+ this . timer . tick ( ) ;
103
+ this . usart . tick ( ) ;
104
+ if ( this . cpu . cycles >= this . nextTick ) {
105
+ callback ( this . cpu ) ;
106
+
107
+ if ( this . stopped ) {
108
+ return ;
109
+ }
110
+
111
+ this . nextTick += this . workUnitCycles ;
112
+
113
+ // We've done our chunk of work, exit the loop
114
+ break ;
115
+ }
116
+ }
117
+
118
+ // Schedule next execution
119
+ setZeroTimeout ( ( ) => this . executeNoAsync ( callback ) ) ;
120
+ }
121
+
59
122
stop ( ) {
60
123
this . stopped = true ;
61
124
}
0 commit comments