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:
BIN
doc/pic/pic.drawio.png
Normal file
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
60
doc/pic/pic.md
Normal 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).
|
||||
|
||||

|
||||
|
||||
## 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
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
82
doc/rtc/rtc.md
Normal 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:
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
32
sw/kernel/devices/interrupt_controller.h
Normal file
32
sw/kernel/devices/interrupt_controller.h
Normal 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
|
||||
136
sw/kernel/devices/interrupt_controller.s
Normal file
136
sw/kernel/devices/interrupt_controller.s
Normal 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
16
sw/kernel/devices/rtc.h
Normal 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
78
sw/kernel/devices/rtc.s
Normal 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
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user