Skip to content

Commit f373d54

Browse files
committed
feat(core): add basic bus, memory, and cpu impl
1 parent fc79f26 commit f373d54

File tree

3 files changed

+155
-0
lines changed

3 files changed

+155
-0
lines changed

src/core/bus/bus.ts

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { asapScheduler, BehaviorSubject, filter, observeOn, shareReplay, skipWhile } from 'rxjs'
2+
3+
export const enum Control {
4+
CLOCK_IDLE = 0b0000,
5+
MEMORY_READ = 0b1000,
6+
IO_READ = 0b1001,
7+
MEMORY_WRITE = 0b1010,
8+
IO_WRITE = 0b1011,
9+
INTERRUPT = 0b1100,
10+
}
11+
12+
export interface Signals {
13+
data: number
14+
address: number
15+
control: Control
16+
}
17+
18+
const initialSignals: Signals = {
19+
data: 0x00,
20+
address: 0x00,
21+
control: Control.CLOCK_IDLE,
22+
}
23+
24+
export class Bus {
25+
private readonly source$ = new BehaviorSubject(initialSignals)
26+
27+
private readonly shared$ = this.source$.pipe(
28+
observeOn(asapScheduler),
29+
shareReplay(1),
30+
)
31+
32+
get signals$() {
33+
return this.shared$
34+
}
35+
36+
get idle$() {
37+
return this.shared$.pipe(
38+
filter((signals) => (signals.control === Control.CLOCK_IDLE)),
39+
)
40+
}
41+
42+
put(next: Partial<Signals>) {
43+
const nextSignals = {
44+
...this.source$.getValue(),
45+
...next,
46+
}
47+
this.source$.next(nextSignals)
48+
return this.shared$.pipe(
49+
skipWhile((signals) => (signals !== nextSignals)),
50+
)
51+
}
52+
}

src/core/cpu/cpu.ts

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { filter, firstValueFrom, map, skipUntil } from 'rxjs'
2+
3+
import { type Bus, Control, type Signals } from '../bus/bus'
4+
5+
export class Cpu {
6+
constructor(
7+
private readonly bus: Bus,
8+
) {
9+
this.bus.signals$.pipe(
10+
filter((signals) => (signals.control === Control.INTERRUPT)),
11+
).subscribe(this.handleInterrupt)
12+
}
13+
14+
async step() {
15+
// TODO: implement step
16+
// const opcode = await this.readMemory(...)
17+
}
18+
19+
private readMemory(address: number) {
20+
const data$ = this.bus.put({
21+
address,
22+
control: Control.MEMORY_READ,
23+
}).pipe(
24+
skipUntil(this.bus.idle$),
25+
map((signals) => signals.data),
26+
)
27+
return firstValueFrom(data$)
28+
}
29+
30+
private writeMemory(address: number, data: number) {
31+
const complete$ = this.bus.put({
32+
data,
33+
address,
34+
control: Control.MEMORY_WRITE,
35+
})
36+
return firstValueFrom(complete$)
37+
}
38+
39+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
40+
private handleInterrupt = (_signals: Signals) => {
41+
// TODO: implement interrupt handling
42+
}
43+
}

src/core/memory/memory.ts

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { filter, map, type Observable, share, tap } from 'rxjs'
2+
3+
import { type Bus, Control, type Signals } from '../bus/bus'
4+
5+
export class Memory {
6+
// TODO: use shared constants
7+
private readonly data = new Uint8Array(0x100)
8+
9+
private readonly read$: Observable<Signals>
10+
private readonly write$: Observable<Signals>
11+
12+
constructor(
13+
private readonly bus: Bus,
14+
) {
15+
this.read$ = this.bus.signals$.pipe(
16+
filter((signals) => signals.control === Control.MEMORY_READ),
17+
tap(this.read),
18+
share(),
19+
)
20+
21+
this.write$ = this.bus.signals$.pipe(
22+
filter((signals) => signals.control === Control.MEMORY_WRITE),
23+
tap(this.write),
24+
share(),
25+
)
26+
27+
this.read$.subscribe()
28+
this.write$.subscribe()
29+
}
30+
31+
private read = (signals: Signals) => {
32+
this.bus.put({
33+
data: this.data[signals.address],
34+
control: Control.CLOCK_IDLE,
35+
})
36+
}
37+
38+
private write = (signals: Signals) => {
39+
this.data[signals.address] = signals.data
40+
this.bus.put({
41+
control: Control.CLOCK_IDLE,
42+
})
43+
}
44+
45+
get data$() {
46+
return this.write$.pipe(map(() => this.getData()))
47+
}
48+
49+
getData() {
50+
return Array.from(this.data)
51+
}
52+
53+
load(data: Uint8Array, offset: number) {
54+
this.data.set(data, offset)
55+
}
56+
57+
reset() {
58+
this.data.fill(0)
59+
}
60+
}

0 commit comments

Comments
 (0)