55 * Copyright (C) 2019, Uri Shaked
66 */
77
8- import { u32 , u16 , u8 } from '../types' ;
8+ import { u32 , u16 , u8 , i16 } from '../types' ;
9+ import { avrInterrupt } from './interrupt' ;
910
1011const registerSpace = 0x100 ;
1112
@@ -45,20 +46,31 @@ export interface CPUMemoryReadHooks {
4546 [ key : number ] : CPUMemoryReadHook ;
4647}
4748
49+ export interface AVRInterruptConfig {
50+ address : u8 ;
51+ enableRegister : u16 ;
52+ enableMask : u8 ;
53+ flagRegister : u16 ;
54+ flagMask : u8 ;
55+ constant ?: boolean ;
56+ }
57+
4858export class CPU implements ICPU {
4959 readonly data : Uint8Array = new Uint8Array ( this . sramBytes + registerSpace ) ;
5060 readonly data16 = new Uint16Array ( this . data . buffer ) ;
5161 readonly dataView = new DataView ( this . data . buffer ) ;
5262 readonly progBytes = new Uint8Array ( this . progMem . buffer ) ;
5363 readonly readHooks : CPUMemoryReadHooks = [ ] ;
5464 readonly writeHooks : CPUMemoryHooks = [ ] ;
65+ private readonly pendingInterrupts : AVRInterruptConfig [ ] = [ ] ;
5566 readonly pc22Bits = this . progBytes . length > 0x20000 ;
5667
5768 // This lets the Timer Compare output override GPIO pins:
5869 readonly gpioTimerHooks : CPUMemoryHooks = [ ] ;
5970
60- pc = 0 ;
61- cycles = 0 ;
71+ pc : u32 = 0 ;
72+ cycles : u32 = 0 ;
73+ nextInterrupt : i16 = - 1 ;
6274
6375 constructor ( public progMem : Uint16Array , private sramBytes = 8192 ) {
6476 this . reset ( ) ;
@@ -67,6 +79,8 @@ export class CPU implements ICPU {
6779 reset ( ) {
6880 this . data . fill ( 0 ) ;
6981 this . SP = this . data . length - 1 ;
82+ this . pendingInterrupts . splice ( 0 , this . pendingInterrupts . length ) ;
83+ this . nextInterrupt = - 1 ;
7084 }
7185
7286 readData ( addr : number ) {
@@ -101,4 +115,63 @@ export class CPU implements ICPU {
101115 get interruptsEnabled ( ) {
102116 return this . SREG & 0x80 ? true : false ;
103117 }
118+
119+ private updateNextInterrupt ( ) {
120+ this . nextInterrupt = this . pendingInterrupts . findIndex ( ( item ) => ! ! item ) ;
121+ }
122+
123+ setInterruptFlag ( interrupt : AVRInterruptConfig ) {
124+ const { flagRegister, flagMask, enableRegister, enableMask } = interrupt ;
125+ if ( interrupt . constant ) {
126+ this . data [ flagRegister ] &= ~ flagMask ;
127+ } else {
128+ this . data [ flagRegister ] |= flagMask ;
129+ }
130+ if ( this . data [ enableRegister ] & enableMask ) {
131+ this . queueInterrupt ( interrupt ) ;
132+ }
133+ }
134+
135+ updateInterruptEnable ( interrupt : AVRInterruptConfig , registerValue : u8 ) {
136+ const { enableMask, flagRegister, flagMask } = interrupt ;
137+ if ( registerValue & enableMask ) {
138+ if ( this . data [ flagRegister ] & flagMask ) {
139+ this . queueInterrupt ( interrupt ) ;
140+ }
141+ } else {
142+ this . clearInterrupt ( interrupt , false ) ;
143+ }
144+ }
145+
146+ queueInterrupt ( interrupt : AVRInterruptConfig ) {
147+ this . pendingInterrupts [ interrupt . address ] = interrupt ;
148+ this . updateNextInterrupt ( ) ;
149+ }
150+
151+ clearInterrupt ( { address, flagRegister, flagMask } : AVRInterruptConfig , clearFlag = true ) {
152+ delete this . pendingInterrupts [ address ] ;
153+ if ( clearFlag ) {
154+ this . data [ flagRegister ] &= ~ flagMask ;
155+ }
156+ this . updateNextInterrupt ( ) ;
157+ }
158+
159+ clearInterruptByFlag ( interrupt : AVRInterruptConfig , registerValue : number ) {
160+ const { flagRegister, flagMask } = interrupt ;
161+ if ( registerValue & flagMask ) {
162+ this . data [ flagRegister ] &= ~ flagMask ;
163+ this . clearInterrupt ( interrupt ) ;
164+ }
165+ }
166+
167+ tick ( ) {
168+ if ( this . interruptsEnabled && this . nextInterrupt >= 0 ) {
169+ const interrupt = this . pendingInterrupts [ this . nextInterrupt ] ;
170+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
171+ avrInterrupt ( this , interrupt . address ) ;
172+ if ( ! interrupt . constant ) {
173+ this . clearInterrupt ( interrupt ) ;
174+ }
175+ }
176+ }
104177}
0 commit comments