Fix delay function, logs in 480i mode, clangd config

This commit is contained in:
spicyjpeg 2023-12-25 15:04:13 +01:00
parent d5c1f0065b
commit 7e7db5b890
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
11 changed files with 149 additions and 42 deletions

9
.clangd Normal file
View File

@ -0,0 +1,9 @@
# As clang/clangd's MIPS-I support is still experimental, some minor changes to
# the GCC arguments it picks up from CMake are required in order to prevent it
# from erroring out. Additionally, specifying the target architecture manually
# fixes some edge cases (such as CMake emitting 8.3 format paths on Windows and
# breaking clangd's target autodetection).
CompileFlags:
Add: [ --target=mipsel-none-elf, -march=mips1 ]
Remove: [ -march, -mno-llsc, -mdivide-breaks ]

1
.gitignore vendored
View File

@ -5,6 +5,7 @@ desktop.ini
# Do not include any built or cached files. # Do not include any built or cached files.
build/ build/
.cache/
__pycache__/ __pycache__/
*.pyc *.pyc
*.pyo *.pyo

View File

@ -336,6 +336,7 @@ Parser *newCartParser(Dump &dump, FormatType formatType, uint8_t flags) {
Parser *newCartParser(Dump &dump) { Parser *newCartParser(Dump &dump) {
// Try all formats from the most complex one to the simplest. // Try all formats from the most complex one to the simplest.
//for (auto &format : _KNOWN_FORMATS) {
for (int i = util::countOf(_KNOWN_FORMATS) - 1; i >= 0; i--) { for (int i = util::countOf(_KNOWN_FORMATS) - 1; i >= 0; i--) {
auto &format = _KNOWN_FORMATS[i]; auto &format = _KNOWN_FORMATS[i];
Parser *parser = newCartParser(dump, format.format, format.flags); Parser *parser = newCartParser(dump, format.format, format.flags);

View File

@ -56,7 +56,7 @@ int _start(int argc, const char **argv) {
// Set $gp to point to the middle of the .sdata/.sbss sections, ensuring // Set $gp to point to the middle of the .sdata/.sbss sections, ensuring
// variables placed in those sections can be quickly accessed. See the // variables placed in those sections can be quickly accessed. See the
// linker script for more details. // linker script for more details.
__asm__ volatile("la $gp, _gp;"); __asm__ volatile("la $gp, _gp\n");
// Set all uninitialized variables to zero by clearing the BSS section. // Set all uninitialized variables to zero by clearing the BSS section.
__builtin_memset(_bssStart, 0, _bssEnd - _bssStart); __builtin_memset(_bssStart, 0, _bssEnd - _bssStart);

View File

@ -19,24 +19,24 @@
#include <stdint.h> #include <stdint.h>
#define COP0_GET(reg, output) \ #define COP0_GET(reg, output) \
__asm__ volatile("mfc0 %0, $%1;" : "=r"(output) : "i"(reg)) __asm__ volatile("mfc0 %0, $%1\n" : "=r"(output) : "i"(reg))
#define COP0_SET(reg, input) \ #define COP0_SET(reg, input) \
__asm__ volatile("mtc0 %0, $%1;" :: "r"(input), "i"(reg)) __asm__ volatile("mtc0 %0, $%1\n" :: "r"(input), "i"(reg))
#define GTE_GET(reg, output) \ #define GTE_GET(reg, output) \
__asm__ volatile("mfc2 %0, $%1;" : "=r"(output) : "i"(reg)) __asm__ volatile("mfc2 %0, $%1\n" : "=r"(output) : "i"(reg))
#define GTE_SET(reg, input) \ #define GTE_SET(reg, input) \
__asm__ volatile("mtc2 %0, $%1;" :: "r"(input), "i"(reg)) __asm__ volatile("mtc2 %0, $%1\n" :: "r"(input), "i"(reg))
#define GTE_GETC(reg, output) \ #define GTE_GETC(reg, output) \
__asm__ volatile("cfc2 %0, $%1;" : "=r"(output) : "i"(reg)) __asm__ volatile("cfc2 %0, $%1\n" : "=r"(output) : "i"(reg))
#define GTE_SETC(reg, input) \ #define GTE_SETC(reg, input) \
__asm__ volatile("ctc2 %0, $%1;" :: "r"(input), "i"(reg)) __asm__ volatile("ctc2 %0, $%1\n" :: "r"(input), "i"(reg))
#define GTE_LOAD(reg, offset, ptr) \ #define GTE_LOAD(reg, offset, ptr) \
__asm__ volatile("lwc2 $%0, %1(%2);" :: "i"(reg), "i"(offset), "r"(ptr)) __asm__ volatile("lwc2 $%0, %1(%2)\n" :: "i"(reg), "i"(offset), "r"(ptr))
#define GTE_STORE(reg, offset, ptr) \ #define GTE_STORE(reg, offset, ptr) \
__asm__ volatile("swc2 $%0, %1(%2);" :: "i"(reg), "i"(offset), "r"(ptr) : "memory") __asm__ volatile("swc2 $%0, %1(%2)\n" :: "i"(reg), "i"(offset), "r"(ptr) : "memory")
/* Coprocessor 0 */ /* Coprocessor 0 */
@ -179,7 +179,7 @@ typedef struct {
} GTEMatrix; } GTEMatrix;
#define gte_command(cmd) \ #define gte_command(cmd) \
__asm__ volatile("nop; nop; cop2 %0;" :: "i"(cmd)) __asm__ volatile("nop\n" "nop\n" "cop2 %0\n" :: "i"(cmd))
/* GTE control registers */ /* GTE control registers */

View File

@ -241,9 +241,9 @@ typedef enum {
TIMER_CTRL_OVERFLOWED = 1 << 12 TIMER_CTRL_OVERFLOWED = 1 << 12
} TimerControlFlag; } TimerControlFlag;
#define TIMER_VALUE(N) _MMIO32((IO_BASE | 0x100) + (16 * (N))) #define TIMER_VALUE(N) _MMIO16((IO_BASE | 0x100) + (16 * (N)))
#define TIMER_CTRL(N) _MMIO32((IO_BASE | 0x104) + (16 * (N))) #define TIMER_CTRL(N) _MMIO16((IO_BASE | 0x104) + (16 * (N)))
#define TIMER_RELOAD(N) _MMIO32((IO_BASE | 0x108) + (16 * (N))) #define TIMER_RELOAD(N) _MMIO16((IO_BASE | 0x108) + (16 * (N)))
/* CD-ROM drive */ /* CD-ROM drive */

View File

@ -89,22 +89,10 @@ void flushCache(void) {
void softReset(void) { void softReset(void) {
disableInterrupts(); disableInterrupts();
BIOS_ENTRY_POINT(); BIOS_ENTRY_POINT();
__builtin_unreachable();
} }
/* IRQ acknowledgement and blocking delay */ /* IRQ acknowledgement */
void delayMicroseconds(int us) {
// 1 us = 33.8688 cycles = 17 loop iterations (2 cycles per iteration)
us *= (F_CPU + 1000000) / 2000000;
__asm__ volatile(
".set noreorder;"
"bgtz %0, .;"
"addiu %0, -1;"
".set reorder;"
: "=r"(us) : "r"(us)
);
}
bool acknowledgeInterrupt(IRQChannel irq) { bool acknowledgeInterrupt(IRQChannel irq) {
if (IRQ_STAT & (1 << irq)) { if (IRQ_STAT & (1 << irq)) {
@ -116,22 +104,22 @@ bool acknowledgeInterrupt(IRQChannel irq) {
} }
bool waitForInterrupt(IRQChannel irq, int timeout) { bool waitForInterrupt(IRQChannel irq, int timeout) {
for (; timeout > 0; timeout--) { for (; timeout > 0; timeout -= 10) {
if (acknowledgeInterrupt(irq)) if (acknowledgeInterrupt(irq))
return true; return true;
delayMicroseconds(1); delayMicroseconds(10);
} }
return false; return false;
} }
bool waitForDMATransfer(DMAChannel dma, int timeout) { bool waitForDMATransfer(DMAChannel dma, int timeout) {
for (; timeout > 0; timeout--) { for (; timeout > 0; timeout -= 10) {
if (!(DMA_CHCR(dma) & DMA_CHCR_ENABLE)) if (!(DMA_CHCR(dma) & DMA_CHCR_ENABLE))
return true; return true;
delayMicroseconds(1); delayMicroseconds(10);
} }
return false; return false;

View File

@ -136,13 +136,24 @@ void softReset(void);
/** /**
* @brief Blocks for (roughly) the specified number of microseconds. This * @brief Blocks for (roughly) the specified number of microseconds. This
* function does not rely on a hardware timer, so interrupts may throw off * function will reset hardware timer 2 and use it for timing. Disabling
* timings if not explicitly disabled prior to calling delayMicroseconds(). * interrupts prior to calling delayMicroseconds() is highly recommended to
* prevent jitter, but not strictly necessary unless the interrupt handler
* accesses timer 2.
* *
* @param time * @param time
*/ */
void delayMicroseconds(int time); void delayMicroseconds(int time);
/**
* @brief Blocks for (roughly) the specified number of microseconds. This
* function does not rely on a hardware timer, so interrupts may throw off
* timings if not explicitly disabled prior to calling delayMicrosecondsBusy().
*
* @param time
*/
void delayMicrosecondsBusy(int time);
/** /**
* @brief Checks if the specified interrupt was fired but not yet acknowledged; * @brief Checks if the specified interrupt was fired but not yet acknowledged;
* if so, acknowledges it and returns true. This function can be used in a * if so, acknowledges it and returns true. This function can be used in a
@ -162,9 +173,9 @@ bool acknowledgeInterrupt(IRQChannel irq);
/** /**
* @brief Waits for the specified interrupt to be fired for up to the specified * @brief Waits for the specified interrupt to be fired for up to the specified
* number of microseconds. This function will work with interrupts that are not * number of microseconds (with 10 us granularity). This function will work with
* explicitly enabled in the IRQ_MASK register, but will *not* work with * interrupts that are not explicitly enabled in the IRQ_MASK register, but will
* interrupts that have been enabled if any callback set using * *not* work with interrupts that have been enabled if any callback set using
* setInterruptHandler() acknowledges them. * setInterruptHandler() acknowledges them.
* *
* @param irq * @param irq
@ -175,7 +186,7 @@ bool waitForInterrupt(IRQChannel irq, int timeout);
/** /**
* @brief Waits for the specified DMA channel to finish any ongoing transfer for * @brief Waits for the specified DMA channel to finish any ongoing transfer for
* up to the specified number of microseconds. * up to the specified number of microseconds (with 10 us granularity).
* *
* @param dma * @param dma
* @param timeout * @param timeout
@ -207,7 +218,7 @@ static inline void switchThreadImmediate(Thread *thread) {
switchThread(thread); switchThread(thread);
// Execute a syscall to force the switch to happen. // Execute a syscall to force the switch to happen.
__asm__ volatile("syscall 0;" ::: "memory"); __asm__ volatile("syscall 0\n" ::: "memory");
} }
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -15,6 +15,8 @@
.set noreorder .set noreorder
.set noat .set noat
## Exception handler
.set BADV, $8 .set BADV, $8
.set SR, $12 .set SR, $12
.set CAUSE, $13 .set CAUSE, $13
@ -179,3 +181,96 @@ _exceptionHandler:
jr $k1 jr $k1
rfe rfe
## Delay functions
.set IO_BASE, 0xbf801000
.set TIMER2_VALUE, IO_BASE | 0x120
.set TIMER2_CTRL, IO_BASE | 0x124
.set TIMER2_RELOAD, IO_BASE | 0x128
.section .text.delayMicroseconds, "ax", @progbits
.global delayMicroseconds
.type delayMicroseconds, @function
delayMicroseconds:
# Calculate the approximate number of CPU cycles that need to be burned,
# assuming a 33.8688 MHz clock (1 us = 33.8688 = ~33.875 cycles).
sll $a1, $a0, 8 # cycles = ((us * 271) + 4) / 8
sll $a2, $a0, 4
addu $a1, $a2
subu $a1, $a0
addiu $a1, 4
sra $a0, $a1, 3
# Compensate for the overhead of calculating the cycle count, entering the
# loop and returning.
addiu $a0, -(6 + 1 + 2 + 3 + 2)
# Reset timer 2 to its default setting of counting system clock edges.
lui $v1, %hi(IO_BASE)
sh $0, %lo(TIMER2_CTRL)($v1) # TIMER2_CTRL = 0
# Wait for up to 0xff00 cycles at a time (resetting the timer and waiting
# for it to count up each time), as the counter is only 16 bits wide. We
# have to wait 0xff00 cycles rather than 0x10000 since the counter wraps
# around rather than saturating on overflow.
li $a1, 0xff00
slt $v0, $a1, $a0
#beqz $v0, .LshortDelay
#nop
beqz $v0, .LskipLongDelay
.LlongDelay: # for (; cycles > 0xff00; cycles -= 0xff00)
sh $0, %lo(TIMER2_VALUE)($v1) # TIMER2_VALUE = 0
li $v0, 0
.LlongDelayLoop: # while (TIMER2_VALUE < 0xff00);
nop
slt $v0, $v0, $a1
bnez $v0, .LlongDelayLoop
lhu $v0, %lo(TIMER2_VALUE)($v1)
slt $v0, $a1, $a0
bnez $v0, .LlongDelay
subu $a0, $a1
.LshortDelay:
# Run the last busy loop once less than 0xff00 cycles are remaining.
sh $0, %lo(TIMER2_VALUE)($v1) # TIMER2_VALUE = 0
.LskipLongDelay:
li $v0, 0
.LshortDelayLoop: # while (TIMER2_VALUE < cycles);
nop
slt $v0, $v0, $a0
bnez $v0, .LshortDelayLoop
lhu $v0, %lo(TIMER2_VALUE)($v1)
jr $ra
nop
.section .text.delayMicrosecondsBusy, "ax", @progbits
.global delayMicrosecondsBusy
.type delayMicrosecondsBusy, @function
delayMicrosecondsBusy:
# Calculate the approximate number of CPU cycles that need to be burned,
# assuming a 33.8688 MHz clock (1 us = 33.8688 = ~33.875 cycles).
sll $a1, $a0, 8 # cycles = ((us * 271) + 4) / 8
sll $a2, $a0, 4
addu $a1, $a2
subu $a1, $a0
addiu $a1, 4
sra $a0, $a1, 3
# Compensate for the overhead of calculating the cycle count and returning.
addiu $a0, -(6 + 1 + 2)
.Lloop: # while (cycles > 0) cycles -= 2
bgtz $a0, .Lloop
addiu $a0, -2
jr $ra
nop

View File

@ -42,8 +42,8 @@ Logger::Logger(void)
} }
void Logger::clear(void) { void Logger::clear(void) {
for (int i = 0; i < MAX_LOG_LINES; i++) for (auto line : _lines)
_lines[i][0] = 0; line[0] = 0;
} }
void Logger::log(const char *format, ...) { void Logger::log(const char *format, ...) {
@ -114,8 +114,10 @@ uint32_t zipCRC32(const uint8_t *data, size_t length, uint32_t crc) {
crc = ~crc; crc = ~crc;
for (; length; length--) { for (; length; length--) {
uint32_t temp = crc;
crc >>= 8; crc >>= 8;
crc ^= table[(crc ^ *(data++)) & 0xff]; crc ^= table[(temp ^ *(data++)) & 0xff];
} }
return ~crc; return ~crc;

View File

@ -224,7 +224,7 @@ public:
/* Logger (basically a ring buffer of lines) */ /* Logger (basically a ring buffer of lines) */
static constexpr int MAX_LOG_LINE_LENGTH = 128; static constexpr int MAX_LOG_LINE_LENGTH = 128;
static constexpr int MAX_LOG_LINES = 32; static constexpr int MAX_LOG_LINES = 64;
class Logger { class Logger {
private: private: