Merge branch '9-initialize-the-devices' into 'master'

Resolve "Initialize the devices"

Closes #9

See merge request bslathi19/super6502!46
This commit is contained in:
Byron Lathi
2023-11-20 15:56:54 +00:00
11 changed files with 423 additions and 17 deletions

BIN
doc/pic/pic.drawio.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

60
doc/pic/pic.md Normal file
View File

@@ -0,0 +1,60 @@
# PIC
## Overview
The PIC (Programmable Interrupt Controller) supports 256 edge or level
triggered interrupts.
Interrupts can only be rising edge or positive level trigger. Negative edge or
low level trigger can be accomplished by inverting the input to this module.
When an interrupt is triggered, it remains triggered until cleared with an
`EOI`. Currently, only the highest priority interrupt can be cleared. This
is bad design and will be fixed soon(tm).
![pic.drawio.png](pic.drawio.png)
## Interface
The PIC is controlled through two 8-bit registers, `CMD` and `DAT`.
The upepr 3 bits of `CMD` control which register is accessed, and the lower 5
bits control the byte select.
`EOI` is a special case: when `CMD` is 0xFF, the highest priority interrupt is
cleared.
### CMD
| [7:5] | [4:0] |
|------------------ |------------- |
| Register Address | Byte Select |
### DAT
| [7:0] |
|----------------------- |
| Data[8*Byte_sel +: 8] |
## Registers
### Register Map
| Address | Read | Write |
|--------- |------------ |-------- |
| 0 | IRQ Number | ~ |
| 1 | Enable | Enable |
| 2 | Type | Type |
### IRQ Number
8 bit register outputs the highest priority interrupt currently active. When
no interrupt is active, it will output 255.
### Enable
256 bit enable register. When bit _n_ is set to 1, the corresponding interrupt
_n_.
### Type
256 bit type register. When bit _n_ is set to 0, the corresponding interrupt
_n_ will be edge triggered. If the bit is set to 1, the interrupt will be level
triggered.

BIN
doc/rtc/rtc.drawio.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

82
doc/rtc/rtc.md Normal file
View File

@@ -0,0 +1,82 @@
# RTC
## Overview
The RTC is capable of keeping track of elapsed time as well as generating
periodic interrupts. Its input frequency is (currently) the same as the CPU
frequency. The architecture is shown below:
![rtc.drawio.png](rtc.drawio.png)
The `counter` register is a 32 bit register which increments by `increment`
every clock tick. When this value reaches `threshold`, both `output` and
`irq_counter` are incremented by 1. When `irq_counter` reaches `irq_threshold`,
an interrupt is generated.
The interrupt frequency can be calculated with the following equations:
$$ F_{out}=\frac{F_{in}\cdot{increment}}{threshold} $$
$$ F_{irq}=\frac{F_{out}}{irq\_threshold} $$
Where $ F_{out} $ is the frequency at which the output changes and $ F_{irq} $
is the frequency at which interrupts occur.
## Interface
The RTC is controlled through two 8-bit registers, `CMD` and `DAT`.
The upepr 4 bits of `CMD` control which register is accessed, and the lower 4
bits control the byte select. The RTC uses 32 bit registers, so only values of
0-3 are supported.
### CMD
| [7:4] | [3:2 | [1:0] |
|------------------ |---------- |------------- |
| Register Address | Reserved | Byte Select |
### DAT
| [7:0] |
|----------------------- |
| Data[8*Byte_sel +: 8] |
## Registers
### Register Map
| Address | Read | Write |
|--------- |--------------- |--------------- |
| 0 | Threshold | Threshold |
| 1 | Increment | Increment |
| 2 | IRQ Threshold | IRQ Threshold |
| 3 | Output | Control |
### Threshold
32 bit threshold register. When `counter` reaches this value, `output` and
`irq_counter` are incremented by 1, and `counter` is reset back to 0.
### Increment
32 bit increment register. Every clock cycle, `counter` is incremented by
`increment`
### IRQ Threshold
32 bit IRQ threshold register. When `irq_counter` reaches this value, and IRQ
is generated and `irq_counter` is reset back to 0.
### Output
32 bit output register. This value ticks up.
### Control
| [7:2] | [1] | [0] |
|---------- |------------ |-------- |
| Reserved | IRQ Enable | Enable |
8 bit Control register. Regardless of Byte Select, any write to address 3 will
write to this register. `IRQ Enable` controls interrupt generation. `Enable` is
a global enable. When 0, all counter registers are set to 0.

View File

@@ -0,0 +1,32 @@
#ifndef _INTERRUPT_CONTROLLER_H
#define _INTERRUPT_CONTROLLER_H
#include <stdint.h>
// These need to be copied in interrupt_controller.s
#define IRQ_CMD_ADDR 0xeffc
#define IRQ_DAT_ADDR 0xeffd
#define IRQ_CMD_MASK 0xe0
#define IRQ_REG_MASK 0x1f
#define IRQ_CMD_READIRQ 0x00
#define IRQ_CMD_ENABLE 0x20
#define IRQ_CMD_TYPE 0x40
#define IRQ_CMD_EOI 0xff
#define IRQ_EDGE 0
#define IRQ_LEVEL 1
void init_interrupt_controller();
void enable_irq(uint8_t type, uint8_t irqnum);
void disable_irq(uint8_t irqnum);
// This should accept irqnum later.
void send_eoi();
#endif

View File

@@ -0,0 +1,136 @@
.MACPACK generic
.autoimport
.importzp tmp1, tmp2
.export _init_interrupt_controller
.export _enable_irq
.export _disable_irq
.export _send_eoi
IRQ_CMD_ADDR = $effc
IRQ_DAT_ADDR = $effd
IRQ_CMD_MASK = $e0
IRQ_REG_MASK = $1f
IRQ_CMD_READIRQ = $00
IRQ_CMD_ENABLE = $20
IRQ_CMD_TYPE = $40
IRQ_CMD_EOI = $ff
.code
; void init_irq();
; mask all IRQs, set all type to edge.
.proc _init_interrupt_controller
ldx #$20 ; enable
ldy #00
jsr cmd_all
ldx #$40 ; edge type
ldy #$00
jsr cmd_all
rts
cmd_all: ; Send the same value to all 32 bytes
txa
add #$20
sta tmp1
loop:
txa
sta IRQ_CMD_ADDR
tya
sta IRQ_DAT_ADDR
inx
cpx tmp1
blt loop
rts
.endproc
; void enable_irq(uint8_t type, uint8_t irqnum);
; in A:
.proc _enable_irq
; Decide which byte we need to modify by dividing by 32 (>> 5)
pha
lsr
lsr
lsr
lsr
lsr ; A is now bytesel
sta tmp2 ; tmp2 is now bytesel
add #IRQ_CMD_ENABLE
sta IRQ_CMD_ADDR
lda IRQ_DAT_ADDR
sta tmp1
pla
and $07 ; A is now 0-7
tax
inx ; X is now 1-8
lda #$01
L1: dex
beq L2
asl
bra L1
L2: pha ; Push bit mask to stack
ora tmp1 ; A is now 1 << (0-7) | enable
sta IRQ_DAT_ADDR
lda tmp2
add #IRQ_CMD_TYPE
sta IRQ_CMD_ADDR
lda IRQ_DAT_ADDR
sta tmp1
jsr popa ; A is now type
beq bit0
bit1: ; set `bit` to 1
pla
ora tmp1
bra L3
bit0: ; set `bit` to 0
pla
eor #$ff
and tmp1
L3: sta IRQ_DAT_ADDR
rts
.endproc
.proc _disable_irq
; Decide which byte we need to modify by dividing by 32 (>> 5)
pha
lsr
lsr
lsr
lsr
lsr ; A is now bytesel
add #IRQ_CMD_ENABLE
sta IRQ_CMD_ADDR
lda IRQ_DAT_ADDR
sta tmp1
pla
and $07 ; A is now 0-7
tax
inx ; X is now 1-8
lda #$01
L1: dex
beq L2
asl
bra L1
L2: eor #$ff ; Invert to set enable to 0
and tmp1 ; a is now ~(1 << (0-7)) & enable
sta IRQ_DAT_ADDR
rts
.endproc
; This should accept irqnum later.
; void send_eoi();
.proc _send_eoi
lda #IRQ_CMD_EOI
sta IRQ_CMD_ADDR
lda #$1
sta IRQ_DAT_ADDR
rts
.endproc

16
sw/kernel/devices/rtc.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef _RTC_H
#define _RTC_H
#include <stdint.h>
#define RTC_CMD_ADDR 0xeffe
#define RTC_DAT_ADDR 0xefff
/* initialize RTC with default values */
void init_rtc(void);
/* handle RTC interrupts */
void handle_rtc(void);
#endif

78
sw/kernel/devices/rtc.s Normal file
View File

@@ -0,0 +1,78 @@
.MACPACK generic
.importzp tmp1
.export _init_rtc
.export _handle_rtc
RTC_CMD = $effe
RTC_DAT = $efff
RTC_THRESHOLD = $00
RTC_INCREMENT = $10
RTC_IRQ_THRESHOLD = $20
RTC_OUTPUT = $30
RTC_CONTROL = $30
; void init_rtc(void);
; Initialize rtc and generate 50ms interrupts
.proc _init_rtc
lda #RTC_INCREMENT+0 ; Set increment to 1
sta RTC_CMD
lda #$01
sta RTC_DAT
lda #RTC_INCREMENT+1
sta RTC_CMD
lda #$00
sta RTC_DAT
lda #RTC_INCREMENT+2
sta RTC_CMD
lda #$00
sta RTC_DAT
lda #RTC_INCREMENT+3
sta RTC_CMD
lda #$00
sta RTC_DAT
lda #RTC_THRESHOLD+0 ; Set threshold to 4000 ($fa0)
sta RTC_CMD
lda #$a0
sta RTC_DAT
lda #RTC_THRESHOLD+1
sta RTC_CMD
lda #$0f
sta RTC_DAT
lda #RTC_THRESHOLD+2
sta RTC_CMD
lda #$00
sta RTC_DAT
lda #RTC_THRESHOLD+3
sta RTC_CMD
lda #$00
sta RTC_DAT
lda #RTC_IRQ_THRESHOLD+0 ; Set irq threshold to 50 ($32)
sta RTC_CMD
lda #$32
sta RTC_DAT
lda #RTC_IRQ_THRESHOLD+1
sta RTC_CMD
lda #$00
sta RTC_DAT
lda #RTC_IRQ_THRESHOLD+2
sta RTC_CMD
lda #$00
sta RTC_DAT
lda #RTC_IRQ_THRESHOLD+3
sta RTC_CMD
lda #$00
sta RTC_DAT
rts
.endproc
.proc _handle_rtc
nop
rti
.endproc

View File

@@ -1,25 +1,27 @@
#include <conio.h>
char* longstring = \
"This is a very long string that is meant to test the loader.\
We can only load one cluster so far, which means 8 sectors of\
512bytes, or a total of 4k. If there was any more data than this,\
then we would have to traverse the fat to find the next cluster number.\
This may not be that difficult, but the file will need to be large\
enough to actually stretch that far. The kernel will probably be\
that big in the future, but for now when it doesnt really do anything\
then it can't really be tested.";
#include "devices/interrupt_controller.h"
#include "devices/rtc.h"
int main() {
char* string = "this is a shorter string";
int val = 42;
cputs("Kernel\n");
cprintf("Kernel printf\n");
cprintf("Val: %d\n", val);
cprintf("%s", string);
cprintf("Here is a long string: %s\n", longstring);
cputs("Kernel\n");
// cputs("Init Paging\n")
// init_paging()
// cputs("Initialize Interrupts\n");
// init_interrupts();
cputs("Initialize Interrupt Controller\n");
init_interrupt_controller();
cputs("Initialize RTC\n");
init_rtc();
// cputs("Initialize Serial\n");
// // init_serial();
// enable_irq(2, IRQ_EDGE);
while(1);