diff options
Diffstat (limited to 'apps/plugins/rockboy/hw.c')
| -rw-r--r-- | apps/plugins/rockboy/hw.c | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/apps/plugins/rockboy/hw.c b/apps/plugins/rockboy/hw.c new file mode 100644 index 0000000..c287e24 --- /dev/null +++ b/apps/plugins/rockboy/hw.c @@ -0,0 +1,183 @@ + + + +#include "rockmacros.h" +#include "defs.h" +#include "cpu.h" +#include "hw.h" +#include "regs.h" +#include "lcd.h" +#include "mem.h" +#include "fastmem.h" + + +struct hw hw; + + + +/* + * hw_interrupt changes the virtual interrupt lines included in the + * specified mask to the values the corresponding bits in i take, and + * in doing so, raises the appropriate bit of R_IF for any interrupt + * lines that transition from low to high. + */ + +void hw_interrupt(byte i, byte mask) +{ + byte oldif = R_IF; + i &= 0x1F & mask; + R_IF |= i & (hw.ilines ^ i); + + /* FIXME - is this correct? not sure the docs understand... */ + if ((R_IF & (R_IF ^ oldif) & R_IE) && cpu.ime) cpu.halt = 0; + /* if ((i & (hw.ilines ^ i) & R_IE) && cpu.ime) cpu.halt = 0; */ + /* if ((i & R_IE) && cpu.ime) cpu.halt = 0; */ + + hw.ilines &= ~mask; + hw.ilines |= i; +} + + +/* + * hw_dma performs plain old memory-to-oam dma, the original dmg + * dma. Although on the hardware it takes a good deal of time, the cpu + * continues running during this mode of dma, so no special tricks to + * stall the cpu are necessary. + */ + +void hw_dma(byte b) +{ + int i; + addr a; + + a = ((addr)b) << 8; + for (i = 0; i < 160; i++, a++) + lcd.oam.mem[i] = readb(a); +} + + + +void hw_hdma_cmd(byte c) +{ + int cnt; + addr sa; + int da; + + /* Begin or cancel HDMA */ + if ((hw.hdma|c) & 0x80) + { + hw.hdma = c; + R_HDMA5 = c & 0x7f; + return; + } + + /* Perform GDMA */ + sa = ((addr)R_HDMA1 << 8) | (R_HDMA2&0xf0); + da = 0x8000 | ((int)(R_HDMA3&0x1f) << 8) | (R_HDMA4&0xf0); + cnt = ((int)c)+1; + /* FIXME - this should use cpu time! */ + /*cpu_timers(102 * cnt);*/ + cnt <<= 4; + while (cnt--) + writeb(da++, readb(sa++)); + R_HDMA1 = sa >> 8; + R_HDMA2 = sa & 0xF0; + R_HDMA3 = 0x1F & (da >> 8); + R_HDMA4 = da & 0xF0; + R_HDMA5 = 0xFF; +} + + +void hw_hdma(void) +{ + int cnt; + addr sa; + int da; + + sa = ((addr)R_HDMA1 << 8) | (R_HDMA2&0xf0); + da = 0x8000 | ((int)(R_HDMA3&0x1f) << 8) | (R_HDMA4&0xf0); + cnt = 16; + while (cnt--) + writeb(da++, readb(sa++)); + R_HDMA1 = sa >> 8; + R_HDMA2 = sa & 0xF0; + R_HDMA3 = 0x1F & (da >> 8); + R_HDMA4 = da & 0xF0; + R_HDMA5--; + hw.hdma--; +} + + +/* + * pad_refresh updates the P1 register from the pad states, generating + * the appropriate interrupts (by quickly raising and lowering the + * interrupt line) if a transition has been made. + */ + +void pad_refresh() +{ + byte oldp1; + oldp1 = R_P1; + R_P1 &= 0x30; + R_P1 |= 0xc0; + if (!(R_P1 & 0x10)) + R_P1 |= (hw.pad & 0x0F); + if (!(R_P1 & 0x20)) + R_P1 |= (hw.pad >> 4); + R_P1 ^= 0x0F; + if (oldp1 & ~R_P1 & 0x0F) + { + hw_interrupt(IF_PAD, IF_PAD); + hw_interrupt(0, IF_PAD); + } +} + + +/* + * These simple functions just update the state of a button on the + * pad. + */ + +void pad_press(byte k) +{ + if (hw.pad & k) + return; + hw.pad |= k; + pad_refresh(); +} + +void pad_release(byte k) +{ + if (!(hw.pad & k)) + return; + hw.pad &= ~k; + pad_refresh(); +} + +void pad_set(byte k, int st) +{ + st ? pad_press(k) : pad_release(k); +} + +void hw_reset() +{ + hw.ilines = hw.pad = 0; + + memset(ram.hi, 0, sizeof ram.hi); + + R_P1 = 0xFF; + R_LCDC = 0x91; + R_BGP = 0xFC; + R_OBP0 = 0xFF; + R_OBP1 = 0xFF; + R_SVBK = 0x01; + R_HDMA5 = 0xFF; + R_VBK = 0xFE; +} + + + + + + + |