From 6640c37a1de7d688d515ee08c17af5fadfcc6e83 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Mon, 23 Jan 2023 16:50:42 -0800 Subject: [PATCH] add several incomplete experiments - add code loading for m68k - incomplete scsp experiments - the register definitions should be fairly complete, though I did not produce actual sound yet - fix type.h definitions - incomplete cdblock register definitions --- .gitignore | 1 + Makefile | 5 +- cdblock.h | 111 ++++++++++++++++++ example/bitmap.c | 66 +++++++++++ example/cell.c | 37 ++++++ example/input.c | 116 +++++++++++++++++++ example/polygon.c | 82 ++++++++++++++ example/sound.c | 30 +++++ m68k.h | 3 + m68k/Makefile | 40 +++++++ m68k/m68k.lds | 35 ++++++ m68k/main.c | 12 ++ m68k/vectors.s | 67 +++++++++++ main.c | 281 ---------------------------------------------- scsp.h | 68 +++++++++++ sh2.lds | 1 + type.h | 14 ++- vdp2.h | 7 +- 18 files changed, 683 insertions(+), 293 deletions(-) create mode 100644 cdblock.h create mode 100644 example/bitmap.c create mode 100644 example/cell.c create mode 100644 example/input.c create mode 100644 example/polygon.c create mode 100644 example/sound.c create mode 100644 m68k.h create mode 100644 m68k/Makefile create mode 100644 m68k/m68k.lds create mode 100644 m68k/main.c create mode 100644 m68k/vectors.s delete mode 100644 main.c create mode 100644 scsp.h diff --git a/.gitignore b/.gitignore index 9a009c5..9f73fda 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ *.elf *.bin *.gch +*.out diff --git a/Makefile b/Makefile index c1a2416..690d11f 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ AARCH = --isa=sh2 --big AFLAGS = -g -gdwarf-4 CFLAGS += -ffunction-sections -fshort-enums -ffreestanding -nostdlib -CFLAGS += -Wall -Werror -Wfatal-errors -g -gdwarf-4 -Og +CFLAGS += -Wall -Werror -Wfatal-errors -Wno-error=unused-variable -g -gdwarf-4 -Og CARCH = -m2 -mb TARGET = sh2-none-elf- @@ -48,8 +48,7 @@ SYS_IP_OBJ += smpsys.o sys_ip.elf: $(SYS_IP_OBJ) -MAIN_OBJ = main.o -#m68k/main.bin.o +MAIN_OBJ = main.o cd.o m68k/main.bin.o main.elf: $(MAIN_OBJ) $(LD) --print-memory-usage -T sh2.lds $^ -o $@ diff --git a/cdblock.h b/cdblock.h new file mode 100644 index 0000000..f16a69c --- /dev/null +++ b/cdblock.h @@ -0,0 +1,111 @@ +#include "type.h" + +struct cr_rr { + reg16 val; + reg16 _res; +}; + +typedef struct cdc_reg { + reg16 DTR; + reg8 _res0[6]; + reg16 HIRQ; + reg16 _res1; + reg16 HMASK; + reg8 _res2[10]; + union { + struct cr_rr CR[4]; + struct cr_rr RR[4]; + }; +} cdc_reg; + +static_assert((offsetof (struct cdc_reg, DTR)) == 0x00); +static_assert((offsetof (struct cdc_reg, HIRQ)) == 0x08); +static_assert((offsetof (struct cdc_reg, HMASK)) == 0x0c); +static_assert((offsetof (struct cdc_reg, RR)) == 0x18); +static_assert((offsetof (struct cdc_reg, RR[0].val)) == 0x18); +static_assert((offsetof (struct cdc_reg, RR[1].val)) == 0x1c); +static_assert((offsetof (struct cdc_reg, RR[2].val)) == 0x20); +static_assert((offsetof (struct cdc_reg, RR[3].val)) == 0x24); +static_assert((offsetof (struct cdc_reg, CR[0].val)) == 0x18); +static_assert((offsetof (struct cdc_reg, CR[1].val)) == 0x1c); +static_assert((offsetof (struct cdc_reg, CR[2].val)) == 0x20); +static_assert((offsetof (struct cdc_reg, CR[3].val)) == 0x24); + +struct cdc { + cdc_reg reg; +}; + +extern struct cdc cdc __asm("cdc"); + +#define CDC__HIRQ__MPST (1 << 13) // MPEG interrupt status +#define CDC__HIRQ__MPCM (1 << 12) // End of MPEG undefined operation section +#define CDC__HIRQ__MPED (1 << 11) // End of MPEG processing +#define CDC__HIRQ__SCDQ (1 << 10) // Subcode Q update complete +#define CDC__HIRQ__EFLS (1 << 9) // End of file system processing +#define CDC__HIRQ__ECPY (1 << 8) // End of copy/move process +#define CDC__HIRQ__EHST (1 << 7) // End of host I/O processing +#define CDC__HIRQ__ESEL (1 << 6) // End of selector setting process +#define CDC__HIRQ__DCHG (1 << 5) // Disk change occured +#define CDC__HIRQ__PEND (1 << 4) // End of CD playback +#define CDC__HIRQ__BFUL (1 << 3) // CD buffer full +#define CDC__HIRQ__CSCT (1 << 2) // 1 sector read complete +#define CDC__HIRQ__DRDY (1 << 1) // Ready for data transfer +#define CDC__HIRQ__CMOK (1 << 0) // Ready for command + +enum +{ + CMD__GET_CDSTATUS = 0x00, + CMD__GET_HWINFO = 0x01, + CMD__GET_TOC = 0x02, + CMD__GET_SESSINFO = 0x03, + CMD__INIT = 0x04, + CMD__OPEN = 0x05, + CMD__END_DATAXFER = 0x06, + + CMD__PLAY = 0x10, + CMD__SEEK = 0x11, + CMD__SCAN = 0x12, + + CMD__GET_SUBCODE = 0x20, + + CMD__SET_CDDEVCONN = 0x30, + CMD__GET_CDDEVCONN = 0x31, + CMD__GET_LASTBUFDST = 0x32, + + CMD__SET_FILTRANGE = 0x40, + CMD__GET_FILTRANGE = 0x41, + CMD__SET_FILTSUBHC = 0x42, + CMD__GET_FILTSUBHC = 0x43, + CMD__SET_FILTMODE = 0x44, + CMD__GET_FILTMODE = 0x45, + CMD__SET_FILTCONN = 0x46, + CMD__GET_FILTCONN = 0x47, + CMD__RESET_SEL = 0x48, + + CMD__GET_BUFSIZE = 0x50, + CMD__GET_SECNUM = 0x51, + CMD__CALC_ACTSIZE = 0x52, + CMD__GET_ACTSIZE = 0x53, + CMD__GET_SECINFO = 0x54, + CMD__EXEC_FADSRCH = 0x55, + CMD__GET_FADSRCH = 0x56, + + CMD__SET_SECLEN = 0x60, + CMD__GET_SECDATA = 0x61, + CMD__DEL_SECDATA = 0x62, + CMD__GETDEL_SECDATA = 0x63, + CMD__PUT_SECDATA = 0x64, + CMD__COPY_SECDATA = 0x65, + CMD__MOVE_SECDATA = 0x66, + CMD__GET_COPYERR = 0x67, + + CMD__CHANGE_DIR = 0x70, + CMD__READ_DIR = 0x71, + CMD__GET_FSSCOPE = 0x72, + CMD__GET_FINFO = 0x73, + CMD__READ_FILE = 0x74, + CMD__ABORT_FILE = 0x75, + + CMD__AUTH_DEVICE = 0xE0, + CMD__GET_AUTH = 0xE1 +}; diff --git a/example/bitmap.c b/example/bitmap.c new file mode 100644 index 0000000..c052c2c --- /dev/null +++ b/example/bitmap.c @@ -0,0 +1,66 @@ +#include "vdp2.h" +#include "vdp1.h" +#include "scu.h" +#include "smpc.h" +#include "sh2.h" +#include "scsp.h" +#include "m68k.h" + +void fill_32(u32 * buf, u32 v, s32 n) +{ + while (n > 0) { + *buf++ = v; + n -= (sizeof (u32)); + } +} + +void copy_16(u16 * src, u16 * dst, s32 n) +{ + while (n > 0) { + *dst++ = *src++; + n -= (sizeof (u16)); + } +} + +static inline void put_pixel(int cx, int cy, u16 color) +{ + #define CW 320 + #define CH 240 + int sx = (CW / 2) + cx; + int sy = (CH / 2) - cy; + vdp2.vram.u16[512 * sy + sx] = (1 << 15) | color; +} + +void start(void) +{ + // DISP: Please make sure to change this bit from 0 to 1 during V blank. + vdp2.reg.TVMD = ( TVMD__DISP | TVMD__LSMD__NON_INTERLACE + | TVMD__VRESO__240 | TVMD__HRESO__NORMAL_320); + + // + // vdp2: bitmap mode + // + + vdp2.reg.BGON = BGON__N0ON; + + vdp2.reg.CHCTLA = ( CHCTLA__N0CHCN__32K_COLOR // 15 bits per pixel, RGB + | CHCTLA__N0BMSZ__512x256_DOT + | CHCTLA__N0BMEN__BITMAP_FORMAT + ); + + vdp2.reg.MPOFN = MPOFN__N0MP(0); // bits 2~0 + // (boundary address value of the bit map pattern) = + // (map offset register value 3 bit) x 20000H + + // zeroize NBG0 (black) + s32 plane_size = 512 * 256 * 2; + fill_32(&vdp2.vram.u32[0x0 / 4], (1 << 15) | (1 << 31), plane_size); + + //vdp2.vram.u16[0x0 / 2] = (1 << 15) | 0x31; + //vdp2.vram.u16[((512 * 239 + 319) * 2) / 2] = (1 << 15) | 0x7fff; + + put_pixel(0, 100, 0x3f); + put_pixel(0, 0, 0x7fff); + + while (1) {} +} diff --git a/example/cell.c b/example/cell.c new file mode 100644 index 0000000..037aa75 --- /dev/null +++ b/example/cell.c @@ -0,0 +1,37 @@ + // + // vdp2: define and place a single character on NBG0 + // + + vdp2.reg.BGON = BGON__N0ON; + + vdp2.reg.CHCTLA = ( CHCTLA__N0CHCN__16_COLOR // 4 bits per pixel, palettized + | CHCTLA__N0BMEN__CELL_FORMAT + | CHCTLA__N0CHSZ__1x1_CELL + ); + + vdp2.reg.PNCN0 = PNCN0__N0PNB__2WORD; + + vdp2.reg.PLSZ = PLSZ__N0PLSZ__1x1; + + vdp2.vram.u16[16 + 0] = (1 << 12); // top left pixel of character # 1 + vdp2.vram.u16[16 + 15] = (2 << 0 ); // bottom right pixel of character # 1 + + vdp2.cram.u16[1] = (0x31 << 5); // green + vdp2.cram.u16[2] = (0x31 << 10); // blue + + // given: + // Plane Size: 1h X 1v + // Pattern Name Data Size: 2 Words + // only bits 5~0 are used for map address calculation + // so MPOFN is effectively ignored + vdp2.reg.MPOFN = MPOFN__N0MP(0); // bits 8~6 + vdp2.reg.MPABN0 = MPABN0__N0MPB(0) | MPABN0__N0MPA(1); // bits 5~0 + vdp2.reg.MPCDN0 = MPABN0__N0MPB(0) | MPABN0__N0MPA(1); // bits 5~0 + + // zeroize NBG0 plane; this should be less than 0x8000 above + s32 plane_size = 64 * 64 * 4; // is this correct ? + fill_32(&vdp2.vram.u32[(0x4000 / 4)], 0, plane_size); + + // Table 4.8 Address value of map designated register by setting + // (bit 5~0) * 0x4000 + vdp2.vram.u32[(0x4000 / 4)] = PATTERN_NAME_TABLE_2WORD__CHARACTER(1); diff --git a/example/input.c b/example/input.c new file mode 100644 index 0000000..a1e8af4 --- /dev/null +++ b/example/input.c @@ -0,0 +1,116 @@ +void v_blank_in_int(void) __attribute__ ((interrupt_handler)); +void v_blank_in_int(void) +{ + scu.reg.IST &= ~(IST__V_BLANK_IN); + + // reset FRC to zero + sh2.reg.FRC.H = 0; + sh2.reg.FRC.L = 0; + + // enable output compare interrupt + sh2.reg.TIER = TIER__OCIAE; +} + +void oci_int(void) __attribute__ ((interrupt_handler)); +void oci_int(void) +{ + // clear OCFA + sh2.reg.FTCSR &= ~(FTCSR__OCFA); + + while (smpc.reg.SF != 0) {} + + smpc.reg.SF = 0; + + smpc.reg.IREG0 = INTBACK__IREG0__STATUS_DISABLE; + smpc.reg.IREG1 = ( INTBACK__IREG1__PERIPHERAL_DATA_ENABLE + | INTBACK__IREG1__PORT2_0BYTE + | INTBACK__IREG1__PORT1_15BYTE + ); + smpc.reg.IREG2 = INTBACK__IREG2__MAGIC; + + smpc.reg.COMREG = COMREG__INTBACK; + + // disable output compare interrupt (to be re-enabled on the next v_blank_in) + sh2.reg.TIER = 0; +} + +void smpc_int(void) __attribute__ ((interrupt_handler)); +void smpc_int(void) +{ + scu.reg.IST &= ~(IST__SMPC); + + if (smpc.reg.SR & SR__PDL) { + // to get all controller data, one should check SR__NPE and send CONTINUE + // requests as needed + + // assuming SR__PDL is set and SR__P1MD is not 0-byte-mode: + // smpc.reg.OREG0 (port 1 status) + // smpc.reg.OREG1 (peripheral 1 data[0] {type,size}) + // smpc.reg.OREG2 (peripheral 1 data[1]) + + if ((smpc.reg.OREG2 & DIGITAL__1__C) == 0) { + // if C is pressed, swap the color palette + vdp2.cram.u16[1] = (0x31 << 10); // blue + vdp2.cram.u16[2] = (0x31 << 5); // green + } else { + // if C is not pressed, restore the original palette + vdp2.cram.u16[1] = (0x31 << 5); // green + vdp2.cram.u16[2] = (0x31 << 10); // blue + } + } + + smpc.reg.IREG0 = INTBACK__IREG0__BREAK; +} + + +// + // scu1: + // + + // If timer0 is counting at roughly 15.73426 kHz (1/63.5556 µs), we can count + // to 300µs at roughly T0C = 5 (~317.778 µs). H-Blank-IN subtracts about + // 10.9µs which is still (~306 µs) more than 300µs. + + vec[SCU_VEC__SMPC] = (u32)(&smpc_int); + vec[SCU_VEC__V_BLANK_IN] = (u32)(&v_blank_in_int); + //scu.reg.T0C = 5; + //scu.reg.T1MD = T1MD__TENB; + + // From the SMPC manual: + // The SMPC uses the V-BLANK-IN interrupt to execute internal tasks. At this + // time, issuing commands for 300 µs from V-BLANK-IN is prohibited. + + // CLKCHG320 (power-on default) NTSC, the FRC's internal clock is 26.8741 MHz. + // The possible periods are then: + // + // - 0.29768 µs (/8) + // - 1.19074 µs (/32) + // - 4.76295 µs (/128) + // + // (1/(26.8741 MHz)) * 128 * 63 = 300.066 µs + + // FRC, OCRA, OCRB, and FCIR are 16-bit registers, but the FRT bus is an 8-bit + // bus. + + // TCR set CKS to /128 + // TOCR set OCRS to OCRA + // TIER set OCIAE + // FTCSR set CCLRA (clear FRC on compare match A) + // VCRC set FOCV + // OCRA set 63 + // FRC set 0 + + vec[0x60] = (u32)&oci_int; + sh2.reg.VCRC = VCRC__FOCV(0x60); + + sh2.reg.TCR = TCR__CKS__INTERNAL_DIV128; + sh2.reg.TOCR = TOCR__OCRS__OCRA; + sh2.reg.FTCSR = FTCSR__CCLRA; + sh2.reg.OCRAB.H = 0; // Even though Kronos doesn't emulate this, SH7095 says + // we are required to write the upper bit prior to + // writing the lower byte + sh2.reg.OCRAB.L = 63; + + // reset/enable interrupts + scu.reg.IST = 0; + scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN); diff --git a/example/polygon.c b/example/polygon.c new file mode 100644 index 0000000..4a8dab0 --- /dev/null +++ b/example/polygon.c @@ -0,0 +1,82 @@ + // + // vdp1: + // + + // The VBE setting must be set immediately after the V-blank IN interrupt. + // + // TVM settings must be performed from the second H-blank IN interrupt after + // the V-blank IN interrupt to the H-blank IN interrupt immediately after the + // V-blank OUT interrupt. + vdp1.reg.TVMR = ( TVMR__TVM__NTSC_PAL + | TVMR__TVM__FRAMEBUFFER_NONROTATION + | TVMR__TVM__16BPP + ); + + // make FCM and FCT settings immediately after V-blank OUT + vdp1.reg.FBCR = 0; + + // "A command table must always be stored at address 00000H to 0001EH." + // we can't trigger a plot yet because we have no (valid) table. + //vdp1.reg.PTMR = PTMR__PTM__FRAME_CHANGE; + + vdp1.reg.EWDR = 0; // black + + // upper-left + vdp1.reg.EWLR = PTMR__EWLR__16BPP_X1(0) | PTMR__EWLR__Y1(0); + + // lower-right + vdp1.reg.EWRR = PTMR__EWRR__16BPP_X3(319) | PTMR__EWRR__Y3(239); + + vdp1.vram.cmd[0].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__USER_CLIP_COORDINATES; + vdp1.vram.cmd[0].LINK = 0; + vdp1.vram.cmd[0].XA = 0; + vdp1.vram.cmd[0].YA = 0; + vdp1.vram.cmd[0].XC = 319; + vdp1.vram.cmd[0].YC = 239; + + vdp1.vram.cmd[1].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__SYSTEM_CLIP_COORDINATES; + vdp1.vram.cmd[1].LINK = 0; + vdp1.vram.cmd[1].XC = 319; + vdp1.vram.cmd[1].YC = 239; + + vdp1.vram.cmd[2].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__LOCAL_COORDINATE; + vdp1.vram.cmd[2].LINK = 0; + vdp1.vram.cmd[2].XA = 0; + vdp1.vram.cmd[2].YA = 0; + + vdp1.vram.cmd[3].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__POLYGON; + vdp1.vram.cmd[3].LINK = 0; + vdp1.vram.cmd[3].PMOD = CTRL__PMOD__ECD | CTRL__PMOD__SPD; + vdp1.vram.cmd[3].COLR = 0x2; // palette color #2 + vdp1.vram.cmd[3].XA = 50; + vdp1.vram.cmd[3].YA = 50; + vdp1.vram.cmd[3].XB = 150; + vdp1.vram.cmd[3].YB = 50; + vdp1.vram.cmd[3].XC = 150; + vdp1.vram.cmd[3].YC = 150; + vdp1.vram.cmd[3].XD = 50; + vdp1.vram.cmd[3].YD = 150; + + vdp1.vram.cmd[4].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__POLYGON; + vdp1.vram.cmd[4].LINK = 0; + vdp1.vram.cmd[4].PMOD = CTRL__PMOD__ECD | CTRL__PMOD__SPD; + vdp1.vram.cmd[4].COLR = (1 << 15) | (0x31 << 10) | (0x31 << 0); // RGB15 magenta + vdp1.vram.cmd[4].XA = 100; + vdp1.vram.cmd[4].YA = 50; + vdp1.vram.cmd[4].XB = 150; + vdp1.vram.cmd[4].YB = 100; + vdp1.vram.cmd[4].XC = 100; + vdp1.vram.cmd[4].YC = 150; + vdp1.vram.cmd[4].XD = 50; + vdp1.vram.cmd[4].YD = 100; + + vdp1.vram.cmd[5].CTRL = CTRL__END; + + // priorities + vdp2.reg.PRISA = 0x0101; + vdp2.reg.PRISB = 0x0101; + vdp2.reg.PRISC = 0x0101; + vdp2.reg.PRISD = 0x0101; + + // start drawing + vdp1.reg.PTMR = PTMR__PTM__FRAME_CHANGE; diff --git a/example/sound.c b/example/sound.c new file mode 100644 index 0000000..8dbb3fe --- /dev/null +++ b/example/sound.c @@ -0,0 +1,30 @@ + // + // scsp + // + + // The SCSP will initialize the internal registers, etc. for about 30µ sec + // after the reset has been released. For this reason, access is not allowed + // during the 30µ sec. + + // Before downloading anything, make sure to set MEM4MB bit to 1 and DAC18B + // bit to 0 within the sound CPU (address) 10400H address. + + smpc.reg.COMREG = COMREG__SNDON; + while (smpc.reg.OREG31 != 0b00000110) {} + for (long i = 0; i < 807; i++) { asm volatile ("nop"); } // wait for (way) more than 30µs + + scsp.reg.common[0] = SCSP__0__MEM4MB | SCSP__0__DAC18B; + + u16 * m68k_main_start = (u16 *)&_binary_m68k_main_bin_start; + u32 m68k_main_size = (u32)&_binary_m68k_main_bin_size; + copy_16(m68k_main_start, &scsp.ram.u16[0], m68k_main_size); + + smpc.reg.COMREG = COMREG__SNDOFF; + while (smpc.reg.OREG31 != 0b00000111) {} + for (long i = 0; i < 807; i++) { asm volatile ("nop"); } // wait for (way) more than 30µs + + smpc.reg.COMREG = COMREG__SNDON; + while (smpc.reg.OREG31 != 0b00000110) {} + for (long i = 0; i < 807; i++) { asm volatile ("nop"); } // wait for (way) more than 30µs + + scsp.reg.common[0] = SCSP__0__MEM4MB | SCSP__0__DAC18B; diff --git a/m68k.h b/m68k.h new file mode 100644 index 0000000..c5dd0ec --- /dev/null +++ b/m68k.h @@ -0,0 +1,3 @@ +extern unsigned long _binary_m68k_main_bin_end __asm("_binary_m68k_main_bin_end"); +extern unsigned long _binary_m68k_main_bin_size __asm("_binary_m68k_main_bin_size"); +extern unsigned long _binary_m68k_main_bin_start __asm("_binary_m68k_main_bin_start"); diff --git a/m68k/Makefile b/m68k/Makefile new file mode 100644 index 0000000..d0f9777 --- /dev/null +++ b/m68k/Makefile @@ -0,0 +1,40 @@ +ARCH = -march=68000 -mcpu=68ec000 +AARCH = $(ARCH) +AFLAGS = -g -gdwarf-4 + +CFLAGS += -ffunction-sections -fshort-enums -ffreestanding -nostdlib +CFLAGS += -Wall -Werror -Wfatal-errors -g -gdwarf-4 -Og +CARCH = $(ARCH) + +TARGET = m68k-none-elf- +CC = $(TARGET)gcc +AS = $(TARGET)as +LD = $(TARGET)ld +OBJCOPY = $(TARGET)objcopy +OBJDUMP = $(TARGET)objdump + +all: main.bin + +%.o: %.s + $(AS) $(AARCH) $(AFLAGS) $< -o $@ + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +%.elf: + $(LD) --print-memory-usage -T m68k.lds $^ -o $@ + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + +MAIN_OBJ = vectors.o main.o + +main.elf: $(MAIN_OBJ) + +clean: + rm -f *.iso *.o *.bin *.elf + +.SUFFIXES: +.INTERMEDIATE: +.SECONDARY: +.PHONY: all clean diff --git a/m68k/m68k.lds b/m68k/m68k.lds new file mode 100644 index 0000000..17cb523 --- /dev/null +++ b/m68k/m68k.lds @@ -0,0 +1,35 @@ +OUTPUT_FORMAT("elf32-m68k", "elf32-m68k", "elf32-m68k") +OUTPUT_ARCH(m68k) +MEMORY +{ + sound_ram : ORIGIN = 0x000000, LENGTH = 512K +} +SECTIONS +{ + .text : + { + . = 0x000000; + KEEP(*(.vectors)) + *(.text) + } > sound_ram + + .data ALIGN(4) : SUBALIGN(4) + { + *(.data) + } > sound_ram + + .rodata ALIGN(4) : SUBALIGN(4) + { + *(.rodata) + } > sound_ram + + .bss ALIGN(4) (NOLOAD) : SUBALIGN(4) + { + *(.bss) + } > sound_ram + + __bss_link_start = ADDR(.bss); + __bss_link_end = ADDR(.bss) + SIZEOF(.bss); +} + +scsp = 0x000000; diff --git a/m68k/main.c b/m68k/main.c new file mode 100644 index 0000000..482f6b2 --- /dev/null +++ b/m68k/main.c @@ -0,0 +1,12 @@ +#include "../scsp.h" + +#define MVOL(n) (n << 0) + +void start(void) +{ + scsp.reg.common[0] = SCSP__0__MEM4MB | SCSP__0__DAC18B | MVOL(15); + + + while (1) { + } +} diff --git a/m68k/vectors.s b/m68k/vectors.s new file mode 100644 index 0000000..05711ce --- /dev/null +++ b/m68k/vectors.s @@ -0,0 +1,67 @@ + .section .vectors + + .long 0x0 /* Reset - initial stack pointer */ + .long start /* Reset - initial program counter */ + .long start /* Bus error */ + .long start /* Address error */ + .long start /* Illegal command */ + .long start /* Divide by zero */ + .long start /* CHK exception */ + .long start /* TRAPV exception */ + .long start /* Privilege violation */ + .long start /* Trace */ + .long start /* Line 1010 emulator */ + .long start /* Line 1111 emulator */ + .long start /* reserved 12 */ + .long start /* reserved 13 */ + .long start /* reserved 14 */ + .long start /* Uninitialized interrupt vector */ + .long start /* reserved 16 */ + .long start /* reserved 17 */ + .long start /* reserved 18 */ + .long start /* reserved 19 */ + .long start /* reserved 20 */ + .long start /* reserved 21 */ + .long start /* reserved 22 */ + .long start /* reserved 23 */ + .long start /* Spurious interrupt */ + .long start /* Auto vector level 1 interrupt */ + .long start /* Auto vector level 2 interrupt */ + .long start /* Auto vector level 3 interrupt */ + .long start /* Auto vector level 4 interrupt */ + .long start /* Auto vector level 5 interrupt */ + .long start /* Auto vector level 6 interrupt */ + .long start /* Auto vector level 7 interrupt */ + .long start /* Trap #0 vector */ + .long start /* Trap #1 vector */ + .long start /* Trap #2 vector */ + .long start /* Trap #3 vector */ + .long start /* Trap #4 vector */ + .long start /* Trap #5 vector */ + .long start /* Trap #6 vector */ + .long start /* Trap #7 vector */ + .long start /* Trap #8 vector */ + .long start /* Trap #9 vector */ + .long start /* Trap #10 vector */ + .long start /* Trap #11 vector */ + .long start /* Trap #12 vector */ + .long start /* Trap #13 vector */ + .long start /* Trap #14 vector */ + .long start /* Trap #15 vector */ + .long start /* reserved 48 */ + .long start /* reserved 49 */ + .long start /* reserved 50 */ + .long start /* reserved 51 */ + .long start /* reserved 52 */ + .long start /* reserved 53 */ + .long start /* reserved 54 */ + .long start /* reserved 55 */ + .long start /* reserved 56 */ + .long start /* reserved 57 */ + .long start /* reserved 58 */ + .long start /* reserved 59 */ + .long start /* reserved 60 */ + .long start /* reserved 61 */ + .long start /* reserved 62 */ + .long start /* reserved 63 */ + .align 0x400, 0xee diff --git a/main.c b/main.c deleted file mode 100644 index 8d36ec0..0000000 --- a/main.c +++ /dev/null @@ -1,281 +0,0 @@ -#include "vdp2.h" -#include "vdp1.h" -#include "scu.h" -#include "smpc.h" -#include "sh2.h" - -void fill_32(u32 * buf, u32 v, s32 n) -{ - while (n > 0) { - *buf = v; - buf += 1; - n -= (sizeof (u32)); - } -} - -void timer0_int(void) __attribute__ ((interrupt_handler)); -void timer0_int(void) -{ - scu.reg.IST &= ~(IST__TIMER0); -} - -void v_blank_in_int(void) __attribute__ ((interrupt_handler)); -void v_blank_in_int(void) -{ - scu.reg.IST &= ~(IST__V_BLANK_IN); - - // reset FRC to zero - sh2.reg.FRC.H = 0; - sh2.reg.FRC.L = 0; - - // enable output compare interrupt - sh2.reg.TIER = TIER__OCIAE; -} - -void oci_int(void) __attribute__ ((interrupt_handler)); -void oci_int(void) -{ - // clear OCFA - sh2.reg.FTCSR &= ~(FTCSR__OCFA); - - while (smpc.reg.SF != 0) {} - - smpc.reg.SF = 0; - - smpc.reg.IREG0 = INTBACK__IREG0__STATUS_DISABLE; - smpc.reg.IREG1 = ( INTBACK__IREG1__PERIPHERAL_DATA_ENABLE - | INTBACK__IREG1__PORT2_0BYTE - | INTBACK__IREG1__PORT1_15BYTE - ); - smpc.reg.IREG2 = INTBACK__IREG2__MAGIC; - - smpc.reg.COMREG = COMREG__INTBACK; - - // disable output compare interrupt (to be re-enabled on the next v_blank_in) - sh2.reg.TIER = 0; -} - -void smpc_int(void) __attribute__ ((interrupt_handler)); -void smpc_int(void) -{ - scu.reg.IST &= ~(IST__SMPC); - - if (smpc.reg.SR & SR__PDL) { - // to get all controller data, one should check SR__NPE and send CONTINUE - // requests as needed - - // assuming SR__PDL is set and SR__P1MD is not 0-byte-mode: - // smpc.reg.OREG0 (port 1 status) - // smpc.reg.OREG1 (peripheral 1 data[0] {type,size}) - // smpc.reg.OREG2 (peripheral 1 data[1]) - - if ((smpc.reg.OREG2 & DIGITAL__1__C) == 0) { - // if C is pressed, swap the color palette - vdp2.cram.u16[1] = (0x31 << 10); // blue - vdp2.cram.u16[2] = (0x31 << 5); // green - } else { - // if C is not pressed, restore the original palette - vdp2.cram.u16[1] = (0x31 << 5); // green - vdp2.cram.u16[2] = (0x31 << 10); // blue - } - } - - smpc.reg.IREG0 = INTBACK__IREG0__BREAK; -} - -void start(void) -{ - // - // vdp2: enable and set Back Screen color - // - - vdp2.reg.TVMD = ( TVMD__DISP | TVMD__BDCLMD | TVMD__LSMD__NON_INTERLACE - | TVMD__VRESO__240 | TVMD__HRESO__NORMAL_320); - - vdp2.reg.BGON = 0; - - // BKTAU/BKTAL are shifted left 1: (0x4000 << 1 = 0x8000) - vdp2.reg.BKTA = REG_UL(BKTAU__BKCLMD_SINGLE_COLOR, 0x4000); - - // background color, rgb15 - vdp2.vram.u16[0x8000 / 2] = (0x05 << 0); // dark red - - // - // vdp2: define and place a single character on NBG0 - // - - vdp2.reg.BGON = BGON__N0ON; - - vdp2.reg.CHCTLA = ( CHCTLA__N0CHCN__16_COLOR // 4 bits per pixel, palettized - | CHCTLA__N0BMSZ__512x512_DOT - | CHCTLA__N0BMEN__CELL_FORMAT - | CHCTLA__N0CHSZ__1x1_CELL - ); - - vdp2.reg.PNCN0 = PNCN0__N0PNB__2WORD; - - vdp2.reg.PLSZ = PLSZ__N0PLSZ__1x1; - - vdp2.vram.u16[16 + 0] = (1 << 12); // top left pixel of character # 1 - vdp2.vram.u16[16 + 15] = (2 << 0 ); // bottom right pixel of character # 1 - - vdp2.cram.u16[1] = (0x31 << 5); // green - vdp2.cram.u16[2] = (0x31 << 10); // blue - - // given: - // Plane Size: 1h X 1v - // Pattern Name Data Size: 2 Words - // only bits 5~0 are used for map address calculation - // so MPOFN is effectively ignored - vdp2.reg.MPOFN = MPOFN__N0MP(0); // bits 8~6 - vdp2.reg.MPABN0 = MPABN0__N0MPB(0) | MPABN0__N0MPA(1); // bits 5~0 - vdp2.reg.MPCDN0 = MPABN0__N0MPB(0) | MPABN0__N0MPA(1); // bits 5~0 - - // zeroize NBG0 plane; this should be less than 0x8000 above - s32 plane_size = 64 * 64 * 4; // is this correct ? - fill_32(&vdp2.vram.u32[(0x4000 / 4)], 0, plane_size); - - // Table 4.8 Address value of map designated register by setting - // (bit 5~0) * 0x4000 - vdp2.vram.u32[(0x4000 / 4)] = PATTERN_NAME_TABLE_2WORD__CHARACTER(1); - - // - // vdp1: - // - - // The VBE setting must be set immediately after the V-blank IN interrupt. - // - // TVM settings must be performed from the second H-blank IN interrupt after - // the V-blank IN interrupt to the H-blank IN interrupt immediately after the - // V-blank OUT interrupt. - vdp1.reg.TVMR = ( TVMR__TVM__NTSC_PAL - | TVMR__TVM__FRAMEBUFFER_NONROTATION - | TVMR__TVM__16BPP - ); - - // make FCM and FCT settings immediately after V-blank OUT - vdp1.reg.FBCR = 0; - - // "A command table must always be stored at address 00000H to 0001EH." - // we can't trigger a plot yet because we have no (valid) table. - //vdp1.reg.PTMR = PTMR__PTM__FRAME_CHANGE; - - vdp1.reg.EWDR = 0; // black - - // upper-left - vdp1.reg.EWLR = PTMR__EWLR__16BPP_X1(0) | PTMR__EWLR__Y1(0); - - // lower-right - vdp1.reg.EWRR = PTMR__EWRR__16BPP_X3(319) | PTMR__EWRR__Y3(239); - - vdp1.vram.cmd[0].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__USER_CLIP_COORDINATES; - vdp1.vram.cmd[0].LINK = 0; - vdp1.vram.cmd[0].XA = 0; - vdp1.vram.cmd[0].YA = 0; - vdp1.vram.cmd[0].XC = 319; - vdp1.vram.cmd[0].YC = 239; - - vdp1.vram.cmd[1].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__SYSTEM_CLIP_COORDINATES; - vdp1.vram.cmd[1].LINK = 0; - vdp1.vram.cmd[1].XC = 319; - vdp1.vram.cmd[1].YC = 239; - - vdp1.vram.cmd[2].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__LOCAL_COORDINATE; - vdp1.vram.cmd[2].LINK = 0; - vdp1.vram.cmd[2].XA = 0; - vdp1.vram.cmd[2].YA = 0; - - vdp1.vram.cmd[3].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__POLYGON; - vdp1.vram.cmd[3].LINK = 0; - vdp1.vram.cmd[3].PMOD = CTRL__PMOD__ECD | CTRL__PMOD__SPD; - vdp1.vram.cmd[3].COLR = 0x2; // palette color #2 - vdp1.vram.cmd[3].XA = 50; - vdp1.vram.cmd[3].YA = 50; - vdp1.vram.cmd[3].XB = 150; - vdp1.vram.cmd[3].YB = 50; - vdp1.vram.cmd[3].XC = 150; - vdp1.vram.cmd[3].YC = 150; - vdp1.vram.cmd[3].XD = 50; - vdp1.vram.cmd[3].YD = 150; - - vdp1.vram.cmd[4].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__POLYGON; - vdp1.vram.cmd[4].LINK = 0; - vdp1.vram.cmd[4].PMOD = CTRL__PMOD__ECD | CTRL__PMOD__SPD; - vdp1.vram.cmd[4].COLR = (1 << 15) | (0x31 << 10) | (0x31 << 0); // RGB15 magenta - vdp1.vram.cmd[4].XA = 100; - vdp1.vram.cmd[4].YA = 50; - vdp1.vram.cmd[4].XB = 150; - vdp1.vram.cmd[4].YB = 100; - vdp1.vram.cmd[4].XC = 100; - vdp1.vram.cmd[4].YC = 150; - vdp1.vram.cmd[4].XD = 50; - vdp1.vram.cmd[4].YD = 100; - - vdp1.vram.cmd[5].CTRL = CTRL__END; - - // priorities - vdp2.reg.PRISA = 0x0101; - vdp2.reg.PRISB = 0x0101; - vdp2.reg.PRISC = 0x0101; - vdp2.reg.PRISD = 0x0101; - - // start drawing - vdp1.reg.PTMR = PTMR__PTM__FRAME_CHANGE; - - // - // scu1: - // - - // If timer0 is counting at roughly 15.73426 kHz (1/63.5556 µs), we can count - // to 300µs at roughly T0C = 5 (~317.778 µs). H-Blank-IN subtracts about - // 10.9µs which is still (~306 µs) more than 300µs. - - vec[SCU_VEC__TIMER0] = (u32)(&timer0_int); - vec[SCU_VEC__SMPC] = (u32)(&smpc_int); - vec[SCU_VEC__V_BLANK_IN] = (u32)(&v_blank_in_int); - //scu.reg.T0C = 5; - //scu.reg.T1MD = T1MD__TENB; - - // From the SMPC manual: - // The SMPC uses the V-BLANK-IN interrupt to execute internal tasks. At this - // time, issuing commands for 300 µs from V-BLANK-IN is prohibited. - - // CLKCHG320 (power-on default) NTSC, the FRC's internal clock is 26.8741 MHz. - // The possible periods are then: - // - // - 0.29768 µs (/8) - // - 1.19074 µs (/32) - // - 4.76295 µs (/128) - // - // (1/(26.8741 MHz)) * 128 * 63 = 300.066 µs - - // FRC, OCRA, OCRB, and FCIR are 16-bit registers, but the FRT bus is an 8-bit - // bus. - - // TCR set CKS to /128 - // TOCR set OCRS to OCRA - // TIER set OCIAE - // FTCSR set CCLRA (clear FRC on compare match A) - // VCRC set FOCV - // OCRA set 63 - // FRC set 0 - - vec[0x60] = (u32)&oci_int; - sh2.reg.VCRC = VCRC__FOCV(0x60); - - sh2.reg.TCR = TCR__CKS__INTERNAL_DIV128; - sh2.reg.TOCR = TOCR__OCRS__OCRA; - sh2.reg.FTCSR = FTCSR__CCLRA; - sh2.reg.OCRAB.H = 0; // Even though Kronos doesn't emulate this, SH7095 says - // we are required to write the upper bit prior to - // writing the lower byte - sh2.reg.OCRAB.L = 63; - - // reset/enable interrupts - scu.reg.IST = 0; - scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN); - - while (1) { - vec[0] = scu.reg.IST; - } -} diff --git a/scsp.h b/scsp.h new file mode 100644 index 0000000..1ed76ea --- /dev/null +++ b/scsp.h @@ -0,0 +1,68 @@ +#include "type.h" + +// Because the main CPU cannot access in units of 8 bits, so read and write in +// 16 bit units. +// +// (presumably m68k can do 32-byte accesses) + +typedef union scsp_ram { + unsigned short u16[0x080000 / 2]; + unsigned long u32[0x080000 / 4]; +} scsp_ram; + +static_assert((sizeof (union scsp_ram)) == 0x080000); + +typedef unsigned char scsp_res0[0x080000]; + +typedef struct scsp_slot { + reg16 field[0x18 / 2]; + reg16 _res[0x8 / 2]; +} scsp_slot; + +static_assert((sizeof (struct scsp_slot)) == 0x20); + +typedef struct scsp_dsp { + reg16 COEF[64]; + reg16 MADRS[32]; + reg16 _res[0x40 / 2]; + reg16 micro[512]; + reg16 internal[370]; +} scsp_dsp; + +static_assert((sizeof (struct scsp_dsp)) == 0x7e4); + +typedef struct scsp_reg { + scsp_slot slot[32]; + reg16 common[0x30 / 2]; + reg16 _res0[0x1d0 / 2]; + struct { + reg16 gen_a[32]; + reg16 gen_b[32]; + } direct; + reg16 _res1[0x80 / 2]; + scsp_dsp dsp; +} scsp_reg; + +static_assert((sizeof (struct scsp_reg)) == 0x000ee4); +static_assert((offsetof (struct scsp_reg, common)) == 0x000400); +static_assert((offsetof (struct scsp_reg, direct)) == 0x000600); +static_assert((offsetof (struct scsp_reg, dsp)) == 0x000700); + +struct scsp { + scsp_ram ram; + scsp_res0 _res0; + scsp_reg reg; +}; + +extern struct scsp scsp __asm("scsp"); + +static_assert((sizeof (struct scsp)) == 0x100ee4); +static_assert((offsetof (struct scsp, ram)) == 0x000000); +static_assert((offsetof (struct scsp, reg)) == 0x100000); + +// bits + +enum scsp_bits { + SCSP__0__MEM4MB = (1 << 9), + SCSP__0__DAC18B = (1 << 8), +}; diff --git a/sh2.lds b/sh2.lds index eba31db..c6ae48f 100644 --- a/sh2.lds +++ b/sh2.lds @@ -35,6 +35,7 @@ SECTIONS } smpc = 0x00100000; +cdb = 0x05890000; scsp = 0x05A00000; vdp1 = 0x05C00000; vdp2 = 0x05E00000; diff --git a/type.h b/type.h index 1861efa..7b889a0 100644 --- a/type.h +++ b/type.h @@ -9,13 +9,15 @@ static_assert((sizeof (reg8)) == 1); static_assert((sizeof (reg16)) == 2); static_assert((sizeof (reg32)) == 4); -typedef volatile unsigned short u8; -typedef volatile short s8; -typedef volatile unsigned short u16; -typedef volatile short s16; -typedef volatile unsigned long u32; -typedef volatile long s32; +typedef unsigned char u8; +typedef char s8; +typedef unsigned short u16; +typedef short s16; +typedef unsigned long u32; +typedef long s32; +static_assert((sizeof (u8)) == 1); +static_assert((sizeof (s8)) == 1); static_assert((sizeof (u16)) == 2); static_assert((sizeof (s16)) == 2); static_assert((sizeof (u32)) == 4); diff --git a/vdp2.h b/vdp2.h index 2c09476..c54b23a 100644 --- a/vdp2.h +++ b/vdp2.h @@ -219,8 +219,9 @@ enum tvmd_bit { enum tvstat_bit { TVSTAT__VBLANK = (1 << 3), }; -// enum vrsize_bit { -// }; +enum vrsize_bit { + VRSIZE__VRAMSZ = (1 << 15), +}; // enum hcnt_bit { // }; // enum vcnt_bit { @@ -292,7 +293,7 @@ enum chctla_bit { CHCTLA__N0BMSZ__1024x512_DOT = (0b11 << 2), CHCTLA__N0BMEN__CELL_FORMAT = (0 << 1), - CHCTLA__N0BMEN__BITMAP_FORMAT = (0 << 1), + CHCTLA__N0BMEN__BITMAP_FORMAT = (1 << 1), CHCTLA__N0CHSZ__1x1_CELL = (0 << 0), CHCTLA__N0CHSZ__2x2_CELL = (1 << 0),