Difference between revisions of "GABE"
(Created page with "== GABE -- The System Control, I/O, and Sound Chip == "GABE" is the name of the system controller chip of the C256 Foenix RevC board, and performs several functions including...") |
(→GABE Random Number Generator Status and Control ($AF:E886)) |
||
(43 intermediate revisions by 4 users not shown) | |||
Line 4: | Line 4: | ||
The RevB version of the C256 had two chips "Gavin" and "Beatrix" that were combined into a single master chip called "GABE". | The RevB version of the C256 had two chips "Gavin" and "Beatrix" that were combined into a single master chip called "GABE". | ||
− | === GABE Control Registers === | + | GABE has several key functions: |
+ | * System start up | ||
+ | * Debug interface control and bus mastering | ||
+ | * I/O interfacing | ||
+ | * Math coprocessor | ||
+ | * Sound chip interfacing and emulation | ||
+ | |||
+ | === GABE System Startup Process === | ||
+ | |||
+ | When the C256 Foenix powers up, GABE takes control and coordinates the startup process. | ||
+ | It first copies the flash data out of flash and into system RAM, starting at bank $38 on the FMX ($18 on the RevB). | ||
+ | GABE also copies the first bank of flash data down to bank $00 of system RAM | ||
+ | before control is given to the CPU and the CPU's RESET is triggered. | ||
+ | This process ensures that the kernel, hardware vectors, and interrupt handlers are all in place prior to the CPU | ||
+ | taking control and resetting the first time on power up. | ||
+ | |||
+ | The mapping of flash memory locations to system RAM is thus: | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | !Flash Range !! RAM Range | ||
+ | |- | ||
+ | |$F8:0000...$FF:FFFF || $38:0000...$3F:FFFF | ||
+ | |- | ||
+ | |$F8:0000...$F8:FFFF || $00:0000...$00:FFFF | ||
+ | |} | ||
+ | |||
+ | === GABE Integer Math Coprocessor ($00:0100 – $00:012B) === | ||
+ | |||
+ | GABE provides an easy to use integer math coprocessor in the form of two 16-multipliers (one signed, one unsigned), | ||
+ | two 16-bit dividers (one signed, one unsigned), and a single 32-bit adder. Using the coprocessor is straight-forward: | ||
+ | the operands are written to the correct registers, and the result is read from the output registers. The operations | ||
+ | are all performed within a single CPU clock cycle, so no waiting is required. | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | !Start Address | ||
+ | !End Address | ||
+ | !Register Name | ||
+ | !Additional Info | ||
+ | |- | ||
+ | | $00:0100 || $00:0101 || UNSIGNED_MULT_A || A operand for unsigned multiplication (16-bit) | ||
+ | |- | ||
+ | | $00:0102 || $00:0103 || UNSIGNED_MULT_B || B operand for unsigned multiplication (16-bit) | ||
+ | |- | ||
+ | | $00:0104 || $00:0107 || UNSIGNED_MULT_RESULT || Result for unsigned multiplication (32-bit) | ||
+ | |- | ||
+ | | $00:0108 || $00:0109 || SIGNED_MULT_A || A operand for signed multiplication (16-bit) | ||
+ | |- | ||
+ | | $00:010A || $00:010B || SIGNED_MULT_B || B operand for signed multiplication (16-bit) | ||
+ | |- | ||
+ | | $00:010C || $00:010F || SIGNED_MULT_RESULT || Result for signed multiplication (32-bit) | ||
+ | |- | ||
+ | | $00:0110 || $00:0111 || UNSIGNED_DIV_DEM || Denominator for unsigned division (16-bit) | ||
+ | |- | ||
+ | | $00:0112 || $00:0113 || UNSIGNED_DIV_NUM || Numerator for unsigned division (16-bit) | ||
+ | |- | ||
+ | | $00:0114 || $00:0115 || UNSIGNED_DIV_QUO || Quotient for unsigned division (16-bit) | ||
+ | |- | ||
+ | | $00:0116 || $00:0117 || UNSIGNED_DIV_REM || Remainder for unsigned division (16-bit) | ||
+ | |- | ||
+ | | $00:0118 || $00:0119 || SIGNED_DIV_DEM || Denominator for signed division (16-bit) | ||
+ | |- | ||
+ | | $00:011A || $00:011B || SIGNED_DIV_NUM || Numerator for signed division (16-bit) | ||
+ | |- | ||
+ | | $00:011C || $00:011D || SIGNED_DIV_QUO || Quotient for signed division (16-bit) | ||
+ | |- | ||
+ | | $00:011E || $00:011F || SIGNED_DIV_REM || Remainder for signed division (16-bit) | ||
+ | |- | ||
+ | | $00:0120 || $00:0123 || ADDER32_A || A operand for 32-bit addition (32-bits) | ||
+ | |- | ||
+ | | $00:0124 || $00:0127 || ADDER32_B || B operand for 32-bit addition (32-bits) | ||
+ | |- | ||
+ | | $00:0128 || $00:012B || ADDER32_R || Result of 32-bit addition (32-bits) | ||
+ | |} | ||
+ | |||
+ | === GABE Keyboard Ports === | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Address !! R/W !! Name !! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0 !! Description | ||
+ | |- | ||
+ | | $AF:1060 || X || KBD_DATA || D7 || D6 || D5 || D4 || D3 || D2 || D1 || D0 || Keyboard data port | ||
+ | |- | ||
+ | | $AF:1061 || X || PORTB || D7 || D6 || D5 || D4 || D3 || D2 || D1 || D0 | ||
+ | |- | ||
+ | | $AF:1064 || R || KBD_STATUS || PAR_E || RX_TO || TX_TO || KBD_INH || CMD || SYS || IN_F || OUT_F || Keyboard Status | ||
+ | |- | ||
+ | | $AF:1064 || W || KBD_CMD || CMD7 || CMD6 || CMD5 || CMD4 || CMD3 || CMD2 || CMD1 || CMD0 || Keyboard Command | ||
+ | |} | ||
+ | |||
+ | ==== GABE Keyboard Status Flags ==== | ||
+ | |||
+ | ;PAR_E | ||
+ | :Parity error | ||
+ | |||
+ | ;RX_TO | ||
+ | :Time out reading data | ||
+ | |||
+ | ;TX_TO | ||
+ | :Time out transmitting data | ||
+ | |||
+ | ;CMD | ||
+ | :If 0, then data written to input buffer is data for PS/2 device. If 1, then data written to input buffer is data for PS/2 controller command | ||
+ | |||
+ | ;SYS | ||
+ | :Flag to the firmware that the keyboard has past POST | ||
+ | |||
+ | ;IN_F | ||
+ | :Input buffer is full | ||
+ | |||
+ | ;OUT_F | ||
+ | :Output buffer is full | ||
+ | |||
+ | === GABE Floating Point Math Coprocessor ($AF:E200 – AFE20F) === | ||
+ | |||
+ | GABE provides a floating point math coprocessor, providing addition, subtraction, multiplication, and division for 32-bit floating point numbers. | ||
+ | Additionally, the coprocessor allows for conversion between a fixed point format and 32-bit IEEE 754 floating point format. | ||
+ | The floating point coprocessor is a little more complex than the integer coprocessor, with each math unit having input multiplexers that control | ||
+ | where the inputs come from, and with the output registers having their own multiplexer. Finally, the output of the multiplier and divider units | ||
+ | can be used directly as inputs to the adder/subtracter. | ||
+ | |||
+ | [[File:FMX Floating Point.jpg|800px|FMX Floating Point Math Coprocessor]] | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Start Address !! End Address !! Register Name !! Additional Information | ||
+ | |- | ||
+ | | $AF:E200 || || FP_MATH_CTRL0 || Controls input multiplexers, Adder function, and Adder multiplexers | ||
+ | |- | ||
+ | | $AF:E201 || || FP_MATH_CTRL1 || Controls output multiplexers | ||
+ | |- | ||
+ | | $AF:E202 || || FP_MATH_CTRL2 || Unused: reserved | ||
+ | |- | ||
+ | | $AF:E203 || || FP_MATH_CTRL3 || Unused: reserved | ||
+ | |- | ||
+ | | $AF:E204 || || FP_MATH_MULT_STAT || Status of the multiplier | ||
+ | |- | ||
+ | | $AF:E205 || || FP_MATH_DIV_STAT || Status of the divider | ||
+ | |- | ||
+ | | $AF:E206 || || FP_MATH_ADD_STAT || Status of the adder/subtracter | ||
+ | |- | ||
+ | | $AF:E207 || || FP_MATH_CONV_STAT || Status of the converters | ||
+ | |- | ||
+ | | $AF:E208 || $AF:E20B || FP_MATH_INPUT0 || Input 0 (write only) | ||
+ | |- | ||
+ | | $AF:E20C || $AF:E20F || FP_MATH_INPUT1 || Input 1 (write only) | ||
+ | |- | ||
+ | | $AF:E208 || $AF:E20B || FP_MATH_OUTPUT_FP || Result in IEEE 754 format (read only) | ||
+ | |- | ||
+ | | $AF:E20C || $AF:E20F || FP_MATH_OUTPUT_FIXED || Result in 20.12 fixed point format (read only) | ||
+ | |} | ||
+ | |||
+ | ==== GABE Register FP_MATH_CTRL0 ($AF:E200) ==== | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0 | ||
+ | |- | ||
+ | |colspan="2"|FP_MATH_CTRL0_ADD_IN1_MUX ||colspan="2"|FP_MATH_CTRL0_ADD_IN0_MUX || FP_MATH_CTRL0_ADD_SUB || || FP_MATH_CTRL0_INPUT1_MUX || FP_MATH_CTRL0_INPUT0_MUX | ||
+ | |} | ||
+ | |||
+ | ; FP_MATH_CTRL0_INPUT0_MUX | ||
+ | : If 0, Input0 is assumed to be in IEEE format. If 1, it is assumed to be in 20.12 fixed point format and will be converted to IEEE. | ||
+ | |||
+ | ; FP_MATH_CTRL0_INPUT1_MUX | ||
+ | : If 0, Input1 is assumed to be in IEEE format. If 1, it is assumed to be in 20.12 fixed point format and will be converted to IEEE. | ||
+ | |||
+ | ; FP_MATH_CTRL0_ADD_SUB | ||
+ | : If 0, the adder will perform a subtract operation. If 1, it will perform an addition. | ||
+ | |||
+ | ; FP_MATH_CTRL0_ADD_IN0_MUX | ||
+ | : Selects the source for input 0 of the adder unit. | ||
+ | |||
+ | ; FP_MATH_CTRL0_ADD_IN1_MUX | ||
+ | : Selects the source for input 1 of the adder unit. | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |+ Adder Source Selector Codes | ||
+ | |- | ||
+ | ! Control Bits !! Input | ||
+ | |- | ||
+ | | 00 || Input 0 | ||
+ | |- | ||
+ | | 01 || Input 1 | ||
+ | |- | ||
+ | | 10 || Output of the multiplier | ||
+ | |- | ||
+ | | 11 || Output of the divider | ||
+ | |} | ||
+ | |||
+ | ==== GABE Register FP_MATH_CTRL1 ($AF:E201) ==== | ||
+ | |||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0 | ||
+ | |- | ||
+ | |colspan="6"| ||colspan="2"| FP_MATH_CTRL1_OUTPUT_MUX0 | ||
+ | |} | ||
+ | |||
+ | ; FP_MATH_CTRL1_OUTPUT_MUX0 | ||
+ | : Selects which functional block provides the output result: | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |+ Result Source Selector Codes | ||
+ | |- | ||
+ | ! Control Bits !! Output Source | ||
+ | |- | ||
+ | | 00 || Result of multiplier is the routed to the output registers | ||
+ | |- | ||
+ | | 01 || Result of divider is the routed to the output registers | ||
+ | |- | ||
+ | | 10 || Result of adder is the routed to the output registers | ||
+ | |- | ||
+ | | 11 || 1.0 is routed to the output registers | ||
+ | |} | ||
+ | |||
+ | ==== GABE Registers: FP Status Registers ($AF:E204 – $AF:E207) ==== | ||
+ | |||
+ | These registers contain the status flags for the various functional units. The status flags are all roughly similar across the four units: | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |+ Status Codes | ||
+ | |- | ||
+ | ! Code !! Meaning | ||
+ | |- | ||
+ | | $01 || Result was not a number (NAN) | ||
+ | |- | ||
+ | | $02 || Overflow | ||
+ | |- | ||
+ | | $04 || Underflow | ||
+ | |- | ||
+ | | $08 || Result was 0.0 | ||
+ | |- | ||
+ | | $10 || Division by 0.0 was attempted | ||
+ | |} | ||
+ | |||
+ | === GABE Timer Registers ($00:0160 – $00:017F) === | ||
+ | |||
+ | * Timer0 and Timer1 count system clock transitions (14318180Hz) | ||
+ | * Timer2 is clocked from VICKY's Start-of-Frame output (50hz). | ||
+ | * To create interrupts at a set interval for timer 0 and 1 divide the clock frequency by the desired interval. So if one wishes 4 interrupts every second use 14318180 divided by 4. | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | !Start Address | ||
+ | !End Address | ||
+ | !Register Name | ||
+ | !Additional Info | ||
+ | |- | ||
+ | !colspan="4" style="text-align:left;" | Timer 0 | ||
+ | |- | ||
+ | | $00:0160 || || TIMER0_CTRL_REG || Timer 0 control register | ||
+ | |- | ||
+ | | $00:0161 || || TIMER0_CHARGE_L || Timer 0 precharge register lower byte, loaded when counting down | ||
+ | |- | ||
+ | | $00:0162 || || TIMER0_CHARGE_M || Timer 0 precharge register middle byte, loaded when counting down | ||
+ | |- | ||
+ | | $00:0163 || || TIMER0_CHARGE_H || Timer 0 precharge register high byte, loaded when counting down | ||
+ | |- | ||
+ | | $00:0164 || || TIMER0_CMP_REG || Timer 0 compare register | ||
+ | |- | ||
+ | | $00:0165 || || TIMER0_CMP_L || Timer 0 compare register lower byte, loaded when counting up | ||
+ | |- | ||
+ | | $00:0166 || || TIMER0_CMP_M || Timer 0 compare register middle byte, loaded when counting up | ||
+ | |- | ||
+ | | $00:0167 || || TIMER0_CMP_H || Timer 0 compare register high byte, loaded when counting up | ||
+ | |- | ||
+ | !colspan="4" style="text-align:left;" | Timer 1 | ||
+ | |- | ||
+ | | $00:0168 || || TIMER1_CTRL_REG || Timer 1 control register | ||
+ | |- | ||
+ | | $00:0169 || || TIMER1_CHARGE_L || Timer 1 precharge register lower byte, loaded when counting down | ||
+ | |- | ||
+ | | $00:016A || || TIMER1_CHARGE_M || Timer 1 precharge register middle byte, loaded when counting down | ||
+ | |- | ||
+ | | $00:016B || || TIMER1_CHARGE_H || Timer 1 precharge register high byte, loaded when counting down | ||
+ | |- | ||
+ | | $00:016C || || TIMER1_CMP_REG || Timer 1 compare register | ||
+ | |- | ||
+ | | $00:016D || || TIMER1_CMP_L || Timer 1 compare register lower byte, loaded when counting up | ||
+ | |- | ||
+ | | $00:016E || || TIMER1_CMP_M || Timer 1 compare register middle byte, loaded when counting up | ||
+ | |- | ||
+ | | $00:016F || || TIMER1_CMP_H || Timer 1 compare register high byte, loaded when counting up | ||
+ | |- | ||
+ | !colspan="4" style="text-align:left;" | Timer 2 | ||
+ | |- | ||
+ | | $00:0170 || || TIMER2_CTRL_REG || Timer 2 control register | ||
+ | |- | ||
+ | | $00:0171 || || TIMER2_CHARGE_L || Timer 2 precharge register lower byte, loaded when counting down | ||
+ | |- | ||
+ | | $00:0172 || || TIMER2_CHARGE_M || Timer 2 precharge register middle byte, loaded when counting down | ||
+ | |- | ||
+ | | $00:0173 || || TIMER2_CHARGE_H || Timer 2 precharge register high byte, loaded when counting down | ||
+ | |- | ||
+ | | $00:0174 || || TIMER2_CMP_REG || Timer 2 compare register | ||
+ | |- | ||
+ | | $00:0175 || || TIMER2_CMP_L || Timer 2 compare register lower byte, loaded when counting up | ||
+ | |- | ||
+ | | $00:0176 || || TIMER2_CMP_M || Timer 2 compare register middle byte, loaded when counting up | ||
+ | |- | ||
+ | | $00:0177 || || TIMER2_CMP_H || Timer 2 compare register high byte, loaded when counting up | ||
+ | |- | ||
+ | |} | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | !Register | ||
+ | !Value | ||
+ | !Name | ||
+ | !Additional Info | ||
+ | |- | ||
+ | | TIMER<X>_CTRL_REG || 0x01 || TMR_EN || Enabled the timer | ||
+ | |- | ||
+ | | TIMER<X>_CTRL_REG || 0x02 || TMR_SCLR || Set when counting up | ||
+ | |- | ||
+ | | TIMER<X>_CTRL_REG || 0x04 || TMR_SLOAD|| Set when counting down | ||
+ | |- | ||
+ | | TIMER<X>_CTRL_REG || 0x08 || TMR_UPDWN|| Set when counting up (1 = Up, 0 = Down) | ||
+ | |- | ||
+ | | TIMER<X>_CMP_REG || 0x01 || TMR_CMP_RECLR|| Set to one for it to cycle when Counting up | ||
+ | |- | ||
+ | | TIMER<X>_CMP_REG || 0x02 || TMR_CMP_RELOAD|| Set to one for it to reload when Counting Down | ||
+ | |- | ||
+ | |} | ||
+ | |||
+ | ==== C pseudo code for counting up ==== | ||
+ | TIMER1_CHARGE_L = 0x00;<br /> | ||
+ | TIMER1_CHARGE_M = 0x00;<br /> | ||
+ | TIMER1_CHARGE_H = 0x00;<br /> | ||
+ | TIMER1_CMP_L = clockValue & 0xFF;<br /> | ||
+ | TIMER1_CMP_M = (clockValue >> 8) & 0xFF;<br /> | ||
+ | TIMER1_CMP_H = (clockValue >> 16) & 0xFF;<br /> | ||
+ | <br /> | ||
+ | TIMER1_CMP_REG = TMR_CMP_RECLR;<br /> | ||
+ | TIMER1_CTRL_REG = TMR_EN | TMR_UPDWN | TMR_SCLR;<br /> | ||
+ | |||
+ | ==== C pseudo code for counting down ==== | ||
+ | TIMER1_CHARGE_L = (clockValue >> 0) & 0xFF;<br /> | ||
+ | TIMER1_CHARGE_M = (clockValue >> 8) & 0xFF;<br /> | ||
+ | TIMER1_CHARGE_H = (clockValue >> 16) & 0xFF;<br /> | ||
+ | TIMER1_CMP_L = 0x00;<br /> | ||
+ | TIMER1_CMP_M = 0x00;<br /> | ||
+ | TIMER1_CMP_H = 0x00;<br /> | ||
+ | <br /> | ||
+ | TIMER1_CMP_REG = TMR_CMP_RELOAD;<br /> | ||
+ | TIMER1_CTRL_REG = TMR_EN | TMR_SLOAD;<br /> | ||
+ | <br /> | ||
+ | |||
+ | === GABE Interrupt Control Registers ($00:0140 – $00:014B) === | ||
+ | |||
+ | There are four types of interrupt control register that GABE provides: pending, polarity, edge detection, and mask. | ||
+ | Each interrupt that is supported has a bit position in each of the 24 or 32 bits provided by the register types. | ||
+ | |||
+ | ; Pending | ||
+ | : The pending registers indicate if an interrupt of a particular type has been triggered and needs processing. An interrupt handler should also write to this register to clear the pending flag, once the interrupt has been processed. | ||
+ | |||
+ | ; Polarity | ||
+ | : This register indicates if the interrupt is triggered by a high or low signal on the input to GABE. | ||
+ | |||
+ | ; Edge | ||
+ | : This register indicates if the interrupt is triggered by an transition (edge) or by a high or low value. | ||
+ | |||
+ | ; Mask | ||
+ | : This register indicates if the associated interrupt will trigger an IRQ to the processor. Interrupt signals with a mask bit of 0 will be ignored, while those with a mask bit of 1 will trigger an interrupt to the CPU. | ||
+ | |||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | !Start | ||
+ | !Register Description | ||
+ | !Additional Info | ||
+ | |- | ||
+ | | $00:0140 || INT_PENDING_REG0 || Interrupt pending #0 | ||
+ | |- | ||
+ | | $00:0141 || INT_PENDING_REG1 || Interrupt pending #1 | ||
+ | |- | ||
+ | | $00:0142 || INT_PENDING_REG2 || Interrupt pending #2 | ||
+ | |- | ||
+ | | $00:0143 || INT_PENDING_REG3 || Interrupt pending #3---FMX Model only | ||
+ | |- | ||
+ | | $00:0144 || INT_POL_REG0 || Interrupt polarity #0 | ||
+ | |- | ||
+ | | $00:0145 || INT_POL_REG1 || Interrupt polarity #1 | ||
+ | |- | ||
+ | | $00:0146 || INT_POL_REG2 || Interrupt polarity #2 | ||
+ | |- | ||
+ | | $00:0147 || INT_POL_REG3 || Interrupt polarity #3---FMX Model only | ||
+ | |- | ||
+ | | $00:0148 || INT_EDGE_REG0 || Enable Edge Detection #0 | ||
+ | |- | ||
+ | | $00:0149 || INT_EDGE_REG1 || Enable Edge Detection #1 | ||
+ | |- | ||
+ | | $00:014A || INT_EDGE_REG2 || Enable Edge Detection #2 | ||
+ | |- | ||
+ | | $00:014B || INT_EDGE_REG3 || Enable Edge Detection #3---FMX Model only | ||
+ | |- | ||
+ | | $00:0148 || INT_MASK_REG0 || Enable Interrupt #0 | ||
+ | |- | ||
+ | | $00:0149 || INT_MASK_REG1 || Enable Interrupt #1 | ||
+ | |- | ||
+ | | $00:014A || INT_MASK_REG2 || Enable Interrupt #2 | ||
+ | |- | ||
+ | | $00:014B || INT_MASK_REG3 || Enable Interrupt #3---FMX Model only | ||
+ | |} | ||
+ | |||
+ | There are several interrupts the GABE can handle, distributed over four blocks: | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | !Block !! Bit !! Name !! Function | ||
+ | |- | ||
+ | | 0 || $01 || FNX0_INT00_SOF || Start of Frame @60Hz | ||
+ | |- | ||
+ | | 0 || $02 || FNX0_INT01_SOL || Start of Line (programmable) | ||
+ | |- | ||
+ | | 0 || $04 || FNX0_INT02_TMR0 || Timer 0 | ||
+ | |- | ||
+ | | 0 || $08 || FNX0_INT03_TMR1 || Timer 1 | ||
+ | |- | ||
+ | | 0 || $10 || FNX0_INT04_TMR2 || Timer 2 | ||
+ | |- | ||
+ | | 0 || $20 || FNX0_INT05_RTC || Real Time Clock | ||
+ | |- | ||
+ | | 0 || $40 || FNX0_INT06_FCC || Floppy Drive Controller | ||
+ | |- | ||
+ | | 0 || $80 || FNX0_INT07_MOUSE || Mouse Interrupt (INT12 in SuperIO IOspace) | ||
+ | |- | ||
+ | | 1 || $01 || FNX1_INT00_KBD || Keyboard Interrupt | ||
+ | |- | ||
+ | | 1 || $02 || FNX1_INT01_SC0 || VICKY_II (INT2) Sprite 2 Sprite Collision | ||
+ | |- | ||
+ | | 1 || $04 || FNX1_INT02_SC1 || VICKY_II (INT3) Sprite 2 Tiles Collision | ||
+ | |- | ||
+ | | 1 || $08 || FNX1_INT03_COM2 || Serial Port 2 (Internal) | ||
+ | |- | ||
+ | | 1 || $10 || FNX1_INT04_COM1 || Serial Port 1 (External) | ||
+ | |- | ||
+ | | 1 || $20 || FNX1_INT05_MPU401 || Midi Controller Interrupt | ||
+ | |- | ||
+ | | 1 || $40 || FNX1_INT06_LPT || Parallel Port | ||
+ | |- | ||
+ | | 1 || $80 || FNX1_INT07_SDCARD || SD Card Controller Interrupt (CH376S, if present) | ||
+ | |- | ||
+ | | 2 || $01 || FNX2_INT00_OPL3 || OPL3 | ||
+ | |- | ||
+ | | 2 || $02 || FNX2_INT01_GABE_INT0 || GABE (INT0) - TBD | ||
+ | |- | ||
+ | | 2 || $04 || FNX2_INT02_GABE_INT1 || GABE (INT1) - TBD | ||
+ | |- | ||
+ | | 2 || $08 || FNX2_INT03_SDMA || VICKY_II (INT4) | ||
+ | |- | ||
+ | | 2 || $10 || FNX2_INT04_VDMA || VICKY_II (INT5) -- Vicky DMA completion | ||
+ | |- | ||
+ | | 2 || $20 || FNX2_INT05_GABE_INT2 || GABE (INT2) - TBD | ||
+ | |- | ||
+ | | 2 || $40 || FNX2_INT06_EXT || External Expansion | ||
+ | |- | ||
+ | | 2 || $80 || FNX2_INT07_SDCARD_INS || SDCARD Insertion | ||
+ | |- | ||
+ | | 3 || $01 || FNX3_INT00_OPN2 || OPN2 | ||
+ | |- | ||
+ | | 3 || $02 || FNX3_INT01_OPM || OPM | ||
+ | |- | ||
+ | | 3 || $04 || FNX3_INT02_IDE || HDD IDE Interrupt | ||
+ | |- | ||
+ | | 3 || $08 || FNX3_INT03_TBD || TBD | ||
+ | |- | ||
+ | | 3 || $10 || FNX3_INT04_TBD || TBD | ||
+ | |- | ||
+ | | 3 || $20 || FNX3_INT05_TBD || TBD | ||
+ | |- | ||
+ | | 3 || $40 || FNX3_INT06_TBD || TBD | ||
+ | |- | ||
+ | | 3 || $80 || FNX3_INT07_TBD || TBD | ||
+ | |} | ||
+ | |||
+ | === GABE SID Emulation ($AF:E400 – $AF:E41F) === | ||
+ | |||
+ | GABE emulates the SID sound chip. Currently, the FMX provides one SID unit. | ||
+ | Although the real SID chip provides analog to digital conversion for two | ||
+ | potentiometer inputs for paddles, the C256's paddle inputs are provided through | ||
+ | a different chip, so the POT registers listed below are not useful. | ||
+ | |||
+ | For complete information on how to use these registers, please consult documentation | ||
+ | on the SID chips. | ||
+ | |||
+ | All registers are write-only, except where noted otherwise. | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Start Address !! End Address !! Register Name !! Additional Information | ||
+ | |- | ||
+ | | $AF:E400 || $AF:E401 || SID0_V1_FREQ || SID0 V1 Frequency (16-bits) | ||
+ | |- | ||
+ | | $AF:E402 || $AF:E403 || SID0_V1_PW || SID0 V1 Pulse Width (12-bits) | ||
+ | |- | ||
+ | | $AF:E404 || || SID0_V1_CTRL || SID0 V1 Control (8-bits) | ||
+ | |- | ||
+ | | $AF:E405 || || SID0_V1_ATCK_DECY || SID0 V1 Attack/Decay (8-bits) | ||
+ | |- | ||
+ | | $AF:E406 || || SID0_V1_SSTN_RLSE || SID0 V1 Sustain/Release (8-bits) | ||
+ | |- | ||
+ | | $AF:E407 || $AF:E408 || SID0_V2_FREQ || SID0 V2 Frequency (16-bits) | ||
+ | |- | ||
+ | | $AF:E409 || $AF:E40A || SID0_V2_PW || SID0 V2 Pulse Width (12-bits) | ||
+ | |- | ||
+ | | $AF:E40B || || SID0_V2_CTRL || SID0 V2 Control (8-bits) | ||
+ | |- | ||
+ | | $AF:E40C || || SID0_V2_ATCK_DECY || SID0 V2 Attack/Decay (8-bits) | ||
+ | |- | ||
+ | | $AF:E40D || || SID0_V2_SSTN_RLSE || SID0 V2 Sustain/Release (8-bits) | ||
+ | |- | ||
+ | | $AF:E40E || $AF:E40F || SID0_V3_FREQ || SID0 V3 Frequency (16-bits) | ||
+ | |- | ||
+ | | $AF:E410 || $AF:E411 || SID0_V3_PW || SID0 V3 Pulse Width (12-bits) | ||
+ | |- | ||
+ | | $AF:E412 || || SID0_V3_CTRL || SID0 V3 Control (8-bits) | ||
+ | |- | ||
+ | | $AF:E413 || || SID0_V3_ATCK_DECY || SID0 V3 Attack/Decay (8-bits) | ||
+ | |- | ||
+ | | $AF:E414 || || SID0_V3_SSTN_RLSE || SID0 V3 Sustain/Release (8-bits) | ||
+ | |- | ||
+ | | $AF:E415 || || SID0_FC_LO || SID0 Filter Frequency LOW (3 bits) | ||
+ | |- | ||
+ | | $AF:E416 || || SID0_FC_HI || SID0 Filter Frequency HIGH (8 bits) | ||
+ | |- | ||
+ | | $AF:E417 || || SID0_RES_FILT || SID0 Resonance / Filter (8-bits) | ||
+ | |- | ||
+ | | $AF:E418 || || SID0_MODE_VOL || SID0 Mode / Volume (8-bits) | ||
+ | |- | ||
+ | | $AF:E419 || || SID0_POT_X || SID0 POT X (Read only, C256 - NOT USED) | ||
+ | |- | ||
+ | | $AF:E41A || || SID0_POT_Y || SID0 POT Y (Read only, C256 - NOT USED) | ||
+ | |- | ||
+ | | $AF:E41B || || SID0_OSC3_RND || SID0 OSC3 / RANDOM (Read only, 8-bits) | ||
+ | |- | ||
+ | | $AF:E41C || || SID0_ENV3 || SID0 ENV3 (Read only, 8-bits) | ||
+ | |} | ||
+ | |||
+ | === GABE Trinity Joystick/Gamepad and DIP Switch Registers ($AF:E800 – $AF:E80E) === | ||
+ | |||
+ | GABE provides access to the Trinity chip, which provides access to the joystick ports and the DIP switches. | ||
+ | The joystick ports can be used in two modes: default mode, where they work like Atari style joysticks, and NES mode | ||
+ | where they can interface with either NES or SNES game pads. | ||
+ | |||
+ | [[File:Joystick Ports.jpg|800px|FMX Joystick Ports Configuration]] | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Address !! Name !! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0 !! Description | ||
+ | |- | ||
+ | | $AF:E800 || JOYSTICK0 || BUTTON2 || BUTTON1 || 0 || BUTTON0 || RIGHT || LEFT || DOWN || UP || Left-most joystick port | ||
+ | |- | ||
+ | | $AF:E801 || JOYSTICK1 || BUTTON2 || BUTTON1 || 0 || BUTTON0 || RIGHT || LEFT || DOWN || UP || Next to joystick port 0 | ||
+ | |- | ||
+ | | $AF:E802 || JOYSTICK2 || 0 || 0 || 0 || BUTTON0 || RIGHT || LEFT || DOWN || UP || Next to joystick port 1 | ||
+ | |- | ||
+ | | $AF:E803 || JOYSTICK3 || 0 || 0 || 0 || BUTTON0 || RIGHT || LEFT || DOWN || UP || Right-most joystick port | ||
+ | |- | ||
+ | | $AF:E804 || JOYSTICK_MODE || NES_TRIG || 0 || 0 || 0 || NES_DONE || NES_JOY || NES_EN1 || NES_EN0 || Control SNES compatibility | ||
+ | |- | ||
+ | | $AF:E805 || REVOFPCB_C ||colspan="8"| "C" || Board identification code byte (expect "C") | ||
+ | |- | ||
+ | | $AF:E806 || REVOFPCB_4 ||colspan="8"| "4" || Board identification code byte (expect "4") | ||
+ | |- | ||
+ | | $AF:E807 || REVOFPCB_A ||colspan="8"| "A" || Board identification code byte (expect "A") | ||
+ | |- | ||
+ | | $AF:E808 || NES_SNES0_DAT_LO ||colspan="8"| see below || Port 0: NES or SNES gamepad data (low byte) | ||
+ | |- | ||
+ | | $AF:E809 || SNES0_DAT_HI ||colspan="8"| see below|| Port 0: NES or SNES gamepad data (highbyte) | ||
+ | |- | ||
+ | | $AF:E80A || NES_SNES1_DAT_LO ||colspan="8"| see below || Port 1: NES or SNES gamepad data (low byte) | ||
+ | |- | ||
+ | | $AF:E80B || SNES1_DAT_HI ||colspan="8"| see below || Port 1: NES or SNES gamepad data (highbyte) | ||
+ | |- | ||
+ | | $AF:E80C || CFP9301_REV ||colspan="8"| revision || Trinity revision code | ||
+ | |- | ||
+ | | $AF:E80D || DIP_USER ||colspan="8"| user || Switches 3 through 5. User defined meaning. | ||
+ | |- | ||
+ | | $AF:E80E || DIP_BOOTMODE || HD_INSTALLED || 0 || 0 || 0 || 0 || 0 || BM1 || BM0 || Boot control switches. | ||
+ | |} | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |+ SNES mode | ||
+ | |- | ||
+ | ! Address !! Name !! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0 !! Description | ||
+ | |- | ||
+ | | $AF:E808 || NES_SNES0_DAT_LO || B || Y || SELECT || START || UP || DOWN || LEFT || RIGHT || NES data if NES_JOY = 1 and NES_EN0 = 1 | ||
+ | |- | ||
+ | | $AF:E809 || SNES0_DAT_HI || 0 || 0 || 0 || 0 || A || X || L || R || NES data if NES_JOY = 1 and NES_EN0 = 1) | ||
+ | |- | ||
+ | | $AF:E80A || NES_SNES1_DAT_LO || B || Y || SELECT || START || UP || DOWN || LEFT || RIGHT || NES data if NES_JOY = 1 and NES_EN1 = 1 | ||
+ | |- | ||
+ | | $AF:E80B || SNES1_DAT_HI || 0 || 0 || 0 || 0 || A || X || L || R || NES data if NES_JOY = 1 and NES_EN1 = 1 | ||
+ | |} | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |+ NES mode | ||
+ | |- | ||
+ | ! Address !! Name !! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0 !! Description | ||
+ | |- | ||
+ | | $AF:E808 || NES_SNES0_DAT_LO || A || B || SELECT || START || UP || DOWN || LEFT || RIGHT || NES data if NES_JOY = 0 and NES_EN0 = 1 | ||
+ | |- | ||
+ | | $AF:E809 || SNES0_DAT_HI ||colspan="2"| N/A || Unused if NES_EN0 = 0 | ||
+ | |- | ||
+ | | $AF:E80A || NES_SNES1_DAT_LO || A || B || SELECT || START || UP || DOWN || LEFT || RIGHT || NES data if NES_JOY = 0 and NES_EN1 = 1 | ||
+ | |- | ||
+ | | $AF:E80B || SNES1_DAT_HI ||colspan="2"| N/A || Unused in NES mode | ||
+ | |} | ||
+ | |||
+ | ==== NES Control Bits ==== | ||
+ | |||
+ | ;NES_TRIG | ||
+ | :Set to one to trigger the serializer to read the gamepad. | ||
+ | |||
+ | ;NES_DONE | ||
+ | :Poll to see if the serializer has finished reading the gamepad. | ||
+ | |||
+ | ;NES_JOY | ||
+ | :If 1, sets SNES compatibility. If 0, sets NES compatibility. | ||
+ | |||
+ | ;NES_EN1 | ||
+ | :If 1, enables NES/SNES compatibility on port 1. If 0, joysticks work like Atari joysticks | ||
+ | |||
+ | ;NES_EN0 | ||
+ | :If 1, enables NES/SNES compatibility on port 0. If 0, joysticks work like Atari joysticks | ||
+ | |||
+ | ==== Boot Mode ==== | ||
+ | |||
+ | Switches 0 and 1 allow you to select the boot mode for the FMX. Note that the switches are active low: | ||
+ | a switch in the "On" position registers as a 0. | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! BM1 || BM0 !! Boot Mode | ||
+ | |- | ||
+ | | 0 || 0 || Boots from the IDE drive DIP switch 8 must be set (HD_INSTALLED). | ||
+ | |- | ||
+ | | 0 || 1 || Boots from the SD card | ||
+ | |- | ||
+ | | 1 || 0 || Boots from the floppy drive | ||
+ | |- | ||
+ | | 1 || 1 || Boots to BASIC | ||
+ | |} | ||
+ | |||
+ | === GABE IDE Registers ($AF:E830 – $AF:E839) === | ||
+ | |||
+ | GABE provides an IDE interface to a hard drive (if installed). This is done through the Unity chip and uses standard IDE interface command, error, and status codes. | ||
+ | |||
+ | {| class="wikitable" | ||
+ | ! Address !! R/W !! Name !! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0 !! Description | ||
+ | |- | ||
+ | | $AF:E830 || x || IDE_DATA || D7 || D6 || D5 || D4 || D3 || D2 || D1 || D0 || 8-bit data read/write | ||
+ | |- | ||
+ | | $AF:E831 || R || IDE_ERROR || BBK || UNC || MC || IDNF || MCR || ABRT || TK0NF || AMNF || Error code for the transaction | ||
+ | |- | ||
+ | | $AF:E832 || W || IDE_SECT_CNT || SC7 || SC6 || SC5 || SC4 || SC3 || SC2 || SC1 || SC0 || Sector count | ||
+ | |- | ||
+ | | $AF:E833 || W || IDE_SECT_SRT || SS7 || SS6 || SS5 || SS4 || SS3 || SS2 || SS1 || SS0 || Sector start (0 = 256), start at 1 | ||
+ | |- | ||
+ | | $AF:E834 || W || IDE_CLDR_LO || C7 || C6 || C5 || C4 || C3 || C2 || C1 || C0 || Cylinder Number (low) | ||
+ | |- | ||
+ | | $AF:E835 || W || IDE_CLDR_HI || C15 || C14 || C13 || C12 || C11 || C10 || C9 || C8 || Cylinder Number (high) | ||
+ | |- | ||
+ | | $AF:E836 || W || IDE_HEAD || 1 || 0 || 1 || M/S || H3 || H2 || H1 || H0 || Head number and Master (0) / Slave(1) device | ||
+ | |- | ||
+ | | $AF:E837 || W || IDE_CMD_STAT || CMD7 || CMD6 || CMD5 || CMD4 || CMD3 || CMD2 || CMD1 || CMD0 || Send a command to the drive | ||
+ | |- | ||
+ | | $AF:E837 || R || IDE_CMD_STAT || BSY || DRDY || DF || DSC || DRQ || CORR || IDX || ERR || Get the status of device. Reading this will clear the Interrupt Registers | ||
+ | |- | ||
+ | | $AF:E838 || X || IDE_DATA_LO || D7 || D6 || D5 || D4 || D3 || D2 || D1 || D0 || 16-bit data transfer registers (low byte) | ||
+ | |- | ||
+ | | $AF:E839 || X || IDE_DATA_HI || D15 || D14 || D13 || D12 || D11 || D10 || D9 || D8 || 16-bit data transfer registers (high byte) | ||
+ | |} | ||
+ | |||
+ | ==== GABE IDE Status Flags ==== | ||
+ | |||
+ | ;BSY | ||
+ | :Device is busy if set | ||
+ | |||
+ | ;DRDY | ||
+ | :Device is ready for a command if set | ||
+ | |||
+ | ;DF | ||
+ | :Device fault | ||
+ | |||
+ | ;DSC | ||
+ | :Device seek complete. If set, the head has stopped moving and is over a track. | ||
+ | |||
+ | ;DRQ | ||
+ | :Device is ready to transfer a data byte (either read or write) if set. | ||
+ | |||
+ | ;CORR | ||
+ | :If set, indicates that data was corrected automatically. | ||
+ | |||
+ | ;IDX | ||
+ | :A vendor specific status | ||
+ | |||
+ | ;ERR | ||
+ | :If set, an error occurred with additional data in the error register. | ||
+ | |||
+ | ==== GABE IDE Error Flags ==== | ||
+ | |||
+ | ;BBK | ||
+ | :Bad block | ||
+ | |||
+ | ;UNC | ||
+ | :Uncorrectable data error | ||
+ | |||
+ | ;MC | ||
+ | :Media changed | ||
+ | |||
+ | ;IDNF | ||
+ | :ID mark not found | ||
+ | |||
+ | ;MCR | ||
+ | :Media change requested | ||
+ | |||
+ | ;ABRT | ||
+ | :Command aborted | ||
+ | |||
+ | ;TK0NF | ||
+ | :Track 0 not found | ||
+ | |||
+ | ;AMNF | ||
+ | :Address mark not found | ||
+ | |||
+ | === GABE Control Registers ($AF:E880 – $AF:E887) === | ||
{| class="wikitable" | {| class="wikitable" | ||
Line 48: | Line 778: | ||
; GABE_CTRL_WRM_RST | ; GABE_CTRL_WRM_RST | ||
: Triggers a warm reset of the board (GABE_RST_AUTH0 must be set to $AD and GABE_RST_AUTH1 to $DE in order to trigger the reset). | : Triggers a warm reset of the board (GABE_RST_AUTH0 must be set to $AD and GABE_RST_AUTH1 to $DE in order to trigger the reset). | ||
+ | |||
+ | ==== GABE Random Number Generator Status and Control ($AF:E886) ==== | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! R/W !! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0 | ||
+ | |- | ||
+ | | R || GABE_RNG_LFSR_DONE || || || || || || || | ||
+ | |- | ||
+ | | W || || || || || || || GABE_RNG_CTRL_DV || GABE_RNG_CTRL_EN | ||
+ | |} | ||
+ | |||
+ | ; GABE_RNG_LFSR_DONE | ||
+ | : Indicates that output = seed database. | ||
+ | |||
+ | ; GABE_RNG_CTRL_DV | ||
+ | : After setting the seed value, toggle this bit for the seed be registered. | ||
+ | |||
+ | ; GABE_RNG_CTRL_EN | ||
+ | : Enable the LFSR block. | ||
==== GABE System Status Register ($AF:E887) ==== | ==== GABE System Status Register ($AF:E887) ==== | ||
Line 79: | Line 829: | ||
| 1 || 1 || Reserved | | 1 || 1 || Reserved | ||
|} | |} | ||
+ | |||
+ | === GABE SDC Interface ($AF:EA00) === | ||
+ | |||
+ | GABE provides block-level access to the SPI interface of the SD card. Currently, there are other registers in that provide access to the SD card through a CH376S chip, but that interface should be considered deprecated in favor of this interface. Most FMX boards do not have the CH376S chip, leaving its position unpopulated on the board. | ||
+ | |||
+ | {| class="wikitable" | ||
+ | ! Address !! R/W !! Name !! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0 !! Description | ||
+ | |- | ||
+ | | $AF:EA00 || R || SDC_VERSION_REG || colspan="8"|version || Should read 12 | ||
+ | |- | ||
+ | | $AF:EA01 || W || SDC_CONTROL_REG || x || x || x || x || x || x || x || RESET || Control register: RESET = 1 resets the core logic. Self clearing. | ||
+ | |- | ||
+ | | $AF:EA02 || W || SDC_TRANS_TYPE_REG ||x || x || x || x || x || x || colspan="2"|TRANS || Sets the type of transaction: 0 = Direct, 1 = INIT SD, 2 = Read Block, 3 = Write block | ||
+ | |- | ||
+ | | $AF:EA03 || W || SDC_TRANS_CONTROL_REG || x || x || x || x || x || x || x || START || Start transaction bit. | ||
+ | |- | ||
+ | | $AF:EA04 || R || SDC_TRANS_STATUS_REG || x || x || x || x || x || x || x || BUSY || Status of the transaction. BUSY = 1 indicates a transaction is in progress. | ||
+ | |- | ||
+ | | $AF:EA05 || R || SDC_TRANS_ERROR_REG || x || x || colspan="2"|WR_ERR || colspan="2"|RD_ERR || colspan="2"|TRANS_ERR || Error conditions for read, write, and transaction. | ||
+ | |- | ||
+ | | $AF:EA06 || RW || SDC_DIRECT_ACCESS_REG || colspan="8"|data || SPI Direct Read and Write - Set DATA before initiating direct Access Transaction | ||
+ | |- | ||
+ | | $AF:EA07 || W || SDC_SD_ADDR_7_0_REG || A7 || A6 || A5 || A4 || A3 || A2 || A1 || A0 || Set the ADDR before a block read or block write. | ||
+ | |- | ||
+ | | $AF:EA08 || W || SDC_SD_ADDR_15_8_REG || A15 || A14 || A13 || A12 || A11 || A10 || A9 || A8 || ADDR[8:0] always should be 0, since each block is 512 bytes | ||
+ | |- | ||
+ | | $AF:EA09 || W || SDC_SD_ADDR_23_16_REG || A23 || A22 || A21 || A20 || A19 || A18 || A7 || A6 | ||
+ | |- | ||
+ | | $AF:EA0A || W || SDC_SD_ADDR_31_24_REG || A32 || A31 || A30 || A29 || A28 || A26 || A25 || A24 | ||
+ | |- | ||
+ | | $AF:EA0B || W || SDC_SPI_CLK_DEL_REG || colspan="8"|clock delay | ||
+ | |- | ||
+ | | $AF:EA10 || R || SDC_RX_FIFO_DATA_REG || colspan="8"|data || Data from block read | ||
+ | |- | ||
+ | | $AF:EA12 || R || SDC_RX_FIFO_DATA_CNT_HI || CNT15 || CNT14 || CNT13 || CNT12 || CNT11 || CNT10 || CNT9 || CNT8 || How many bytes in the read FIFO (HI) | ||
+ | |- | ||
+ | | $AF:EA13 || R || SDC_RX_FIFO_DATA_CNT_LO || CNT7 || CNT6 || CNT5 || CNT4 || CNT3 || CNT2 || CNT1 || CNT0 || How many bytes in the read FIFO (LOW) | ||
+ | |- | ||
+ | | $AF:EA14 || W || SDC_RX_FIFO_CTRL_REG || x || x || x || x || x || x || x || CLEAR || CLEAR = 1, clear the read FIFO. Bit is self-clearing. | ||
+ | |- | ||
+ | | $AF:EA20 || W || SDC_TX_FIFO_DATA_REG || colspan="8"|data || Write data block here. | ||
+ | |- | ||
+ | | $AF:EA21 || W || SDC_TX_FIFO_CTRL_REG || x || x || x || x || x || x || x || CLEAR || CLEAR = 1, clear the write FIFO. Bit is self-clearing. | ||
+ | |} | ||
+ | |||
+ | ==== GABE: SDC_TRANS_ERROR_REG ($AF:EA05) ==== | ||
+ | |||
+ | Lists any error conditions on reads, writes, or general transactions: | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |+ TRANS_ERR codes [1:0] | ||
+ | ! Code !! Condition | ||
+ | |- | ||
+ | | 00 || No INIT error | ||
+ | |- | ||
+ | | 01 || INIT CMD0 error | ||
+ | |- | ||
+ | | 10 || INIT CMD1 error | ||
+ | |} | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |+ RD_ERR codes [3:2] | ||
+ | ! Code !! Condition | ||
+ | |- | ||
+ | | 00 || No read error | ||
+ | |- | ||
+ | | 01 || CMD error | ||
+ | |- | ||
+ | | 10 || Token error | ||
+ | |} | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |+ WR_ERR codes [5:4] | ||
+ | ! Code !! Condition | ||
+ | |- | ||
+ | | 00 || No write error | ||
+ | |- | ||
+ | | 01 || CMD error | ||
+ | |- | ||
+ | | 10 || Data error | ||
+ | |- | ||
+ | | 11 || Busy error | ||
+ | |} | ||
+ | |||
+ | === GABE SN76489 Interface ($AF:F100) === | ||
+ | |||
+ | GABE provides the interface to the SN76489 sound chip on the Foenix FMX. This chip uses only a single write-only address: $AF:F100. The SN76489 has four voices: three tone generators, and one noise generator. Each voice has a frequency and an attenuation setting. For full details on how to use the SN76489 chip, please consult the official documentation, but here is a brief summary. | ||
+ | |||
+ | ==== Playing a Tone ==== | ||
+ | |||
+ | To change the frequency of a tone generator, two bytes are sent to the chip in order: | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! !! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0 | ||
+ | |- | ||
+ | | First || 1 ||colspan="2"| Channel || 0 || F3 || F2 || F1 || F0 | ||
+ | |- | ||
+ | | Second || 0 || x || F9 || F8 || F7 || F6 || F5 || F4 | ||
+ | |} | ||
+ | |||
+ | The actual frequency played depends on the clock driving the chip: freq = N / (32 * F), where N = 3575800 (for the FMX), and F is the 10-bit number sent to the chip. The "Channel" number above must be 0, 1, or 3. | ||
+ | |||
+ | ==== Play a Noise ==== | ||
+ | |||
+ | To play a noise, only a single byte is written to the chip: | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0 | ||
+ | |- | ||
+ | | 1 || 1 || 1 || 0 || x || NF|| NF1 || NF0 | ||
+ | |} | ||
+ | |||
+ | The NF bit controls the type of noise generated, and the NF bits control the base frequency of the noise. | ||
+ | |||
+ | ==== Setting the Volume ==== | ||
+ | |||
+ | The volumes of each of the four voices can be set independently. To set the volume of a single voice, a single byte is sent to the chip: | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! 7 !! 6 !! 5 !! 4 !! 3 !! 2 !! 1 !! 0 | ||
+ | |- | ||
+ | | 1 ||colspan="2"| Channel || 1 || A3 || A2 || A1 || A0 | ||
+ | |} | ||
+ | |||
+ | The "Channel" bits are the number of the voice to change: 0, 1, 2, or 3. The four bit value A is the level of attenuation, where 0 is fully on, and 15 is fully off. |
Latest revision as of 07:16, 2 August 2021
Contents
- 1 GABE -- The System Control, I/O, and Sound Chip
- 1.1 GABE System Startup Process
- 1.2 GABE Integer Math Coprocessor ($00:0100 – $00:012B)
- 1.3 GABE Keyboard Ports
- 1.4 GABE Floating Point Math Coprocessor ($AF:E200 – AFE20F)
- 1.5 GABE Timer Registers ($00:0160 – $00:017F)
- 1.6 GABE Interrupt Control Registers ($00:0140 – $00:014B)
- 1.7 GABE SID Emulation ($AF:E400 – $AF:E41F)
- 1.8 GABE Trinity Joystick/Gamepad and DIP Switch Registers ($AF:E800 – $AF:E80E)
- 1.9 GABE IDE Registers ($AF:E830 – $AF:E839)
- 1.10 GABE Control Registers ($AF:E880 – $AF:E887)
- 1.11 GABE SDC Interface ($AF:EA00)
- 1.12 GABE SN76489 Interface ($AF:F100)
GABE -- The System Control, I/O, and Sound Chip
"GABE" is the name of the system controller chip of the C256 Foenix RevC board, and performs several functions including system bus management, math coprocessing, I/O, and sound. The RevB version of the C256 had two chips "Gavin" and "Beatrix" that were combined into a single master chip called "GABE".
GABE has several key functions:
- System start up
- Debug interface control and bus mastering
- I/O interfacing
- Math coprocessor
- Sound chip interfacing and emulation
GABE System Startup Process
When the C256 Foenix powers up, GABE takes control and coordinates the startup process. It first copies the flash data out of flash and into system RAM, starting at bank $38 on the FMX ($18 on the RevB). GABE also copies the first bank of flash data down to bank $00 of system RAM before control is given to the CPU and the CPU's RESET is triggered. This process ensures that the kernel, hardware vectors, and interrupt handlers are all in place prior to the CPU taking control and resetting the first time on power up.
The mapping of flash memory locations to system RAM is thus:
Flash Range | RAM Range |
---|---|
$F8:0000...$FF:FFFF | $38:0000...$3F:FFFF |
$F8:0000...$F8:FFFF | $00:0000...$00:FFFF |
GABE Integer Math Coprocessor ($00:0100 – $00:012B)
GABE provides an easy to use integer math coprocessor in the form of two 16-multipliers (one signed, one unsigned), two 16-bit dividers (one signed, one unsigned), and a single 32-bit adder. Using the coprocessor is straight-forward: the operands are written to the correct registers, and the result is read from the output registers. The operations are all performed within a single CPU clock cycle, so no waiting is required.
Start Address | End Address | Register Name | Additional Info |
---|---|---|---|
$00:0100 | $00:0101 | UNSIGNED_MULT_A | A operand for unsigned multiplication (16-bit) |
$00:0102 | $00:0103 | UNSIGNED_MULT_B | B operand for unsigned multiplication (16-bit) |
$00:0104 | $00:0107 | UNSIGNED_MULT_RESULT | Result for unsigned multiplication (32-bit) |
$00:0108 | $00:0109 | SIGNED_MULT_A | A operand for signed multiplication (16-bit) |
$00:010A | $00:010B | SIGNED_MULT_B | B operand for signed multiplication (16-bit) |
$00:010C | $00:010F | SIGNED_MULT_RESULT | Result for signed multiplication (32-bit) |
$00:0110 | $00:0111 | UNSIGNED_DIV_DEM | Denominator for unsigned division (16-bit) |
$00:0112 | $00:0113 | UNSIGNED_DIV_NUM | Numerator for unsigned division (16-bit) |
$00:0114 | $00:0115 | UNSIGNED_DIV_QUO | Quotient for unsigned division (16-bit) |
$00:0116 | $00:0117 | UNSIGNED_DIV_REM | Remainder for unsigned division (16-bit) |
$00:0118 | $00:0119 | SIGNED_DIV_DEM | Denominator for signed division (16-bit) |
$00:011A | $00:011B | SIGNED_DIV_NUM | Numerator for signed division (16-bit) |
$00:011C | $00:011D | SIGNED_DIV_QUO | Quotient for signed division (16-bit) |
$00:011E | $00:011F | SIGNED_DIV_REM | Remainder for signed division (16-bit) |
$00:0120 | $00:0123 | ADDER32_A | A operand for 32-bit addition (32-bits) |
$00:0124 | $00:0127 | ADDER32_B | B operand for 32-bit addition (32-bits) |
$00:0128 | $00:012B | ADDER32_R | Result of 32-bit addition (32-bits) |
GABE Keyboard Ports
Address | R/W | Name | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Description |
---|---|---|---|---|---|---|---|---|---|---|---|
$AF:1060 | X | KBD_DATA | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | Keyboard data port |
$AF:1061 | X | PORTB | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | |
$AF:1064 | R | KBD_STATUS | PAR_E | RX_TO | TX_TO | KBD_INH | CMD | SYS | IN_F | OUT_F | Keyboard Status |
$AF:1064 | W | KBD_CMD | CMD7 | CMD6 | CMD5 | CMD4 | CMD3 | CMD2 | CMD1 | CMD0 | Keyboard Command |
GABE Keyboard Status Flags
- PAR_E
- Parity error
- RX_TO
- Time out reading data
- TX_TO
- Time out transmitting data
- CMD
- If 0, then data written to input buffer is data for PS/2 device. If 1, then data written to input buffer is data for PS/2 controller command
- SYS
- Flag to the firmware that the keyboard has past POST
- IN_F
- Input buffer is full
- OUT_F
- Output buffer is full
GABE Floating Point Math Coprocessor ($AF:E200 – AFE20F)
GABE provides a floating point math coprocessor, providing addition, subtraction, multiplication, and division for 32-bit floating point numbers. Additionally, the coprocessor allows for conversion between a fixed point format and 32-bit IEEE 754 floating point format. The floating point coprocessor is a little more complex than the integer coprocessor, with each math unit having input multiplexers that control where the inputs come from, and with the output registers having their own multiplexer. Finally, the output of the multiplier and divider units can be used directly as inputs to the adder/subtracter.
Start Address | End Address | Register Name | Additional Information |
---|---|---|---|
$AF:E200 | FP_MATH_CTRL0 | Controls input multiplexers, Adder function, and Adder multiplexers | |
$AF:E201 | FP_MATH_CTRL1 | Controls output multiplexers | |
$AF:E202 | FP_MATH_CTRL2 | Unused: reserved | |
$AF:E203 | FP_MATH_CTRL3 | Unused: reserved | |
$AF:E204 | FP_MATH_MULT_STAT | Status of the multiplier | |
$AF:E205 | FP_MATH_DIV_STAT | Status of the divider | |
$AF:E206 | FP_MATH_ADD_STAT | Status of the adder/subtracter | |
$AF:E207 | FP_MATH_CONV_STAT | Status of the converters | |
$AF:E208 | $AF:E20B | FP_MATH_INPUT0 | Input 0 (write only) |
$AF:E20C | $AF:E20F | FP_MATH_INPUT1 | Input 1 (write only) |
$AF:E208 | $AF:E20B | FP_MATH_OUTPUT_FP | Result in IEEE 754 format (read only) |
$AF:E20C | $AF:E20F | FP_MATH_OUTPUT_FIXED | Result in 20.12 fixed point format (read only) |
GABE Register FP_MATH_CTRL0 ($AF:E200)
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
FP_MATH_CTRL0_ADD_IN1_MUX | FP_MATH_CTRL0_ADD_IN0_MUX | FP_MATH_CTRL0_ADD_SUB | FP_MATH_CTRL0_INPUT1_MUX | FP_MATH_CTRL0_INPUT0_MUX |
- FP_MATH_CTRL0_INPUT0_MUX
- If 0, Input0 is assumed to be in IEEE format. If 1, it is assumed to be in 20.12 fixed point format and will be converted to IEEE.
- FP_MATH_CTRL0_INPUT1_MUX
- If 0, Input1 is assumed to be in IEEE format. If 1, it is assumed to be in 20.12 fixed point format and will be converted to IEEE.
- FP_MATH_CTRL0_ADD_SUB
- If 0, the adder will perform a subtract operation. If 1, it will perform an addition.
- FP_MATH_CTRL0_ADD_IN0_MUX
- Selects the source for input 0 of the adder unit.
- FP_MATH_CTRL0_ADD_IN1_MUX
- Selects the source for input 1 of the adder unit.
Control Bits | Input |
---|---|
00 | Input 0 |
01 | Input 1 |
10 | Output of the multiplier |
11 | Output of the divider |
GABE Register FP_MATH_CTRL1 ($AF:E201)
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
FP_MATH_CTRL1_OUTPUT_MUX0 |
- FP_MATH_CTRL1_OUTPUT_MUX0
- Selects which functional block provides the output result:
Control Bits | Output Source |
---|---|
00 | Result of multiplier is the routed to the output registers |
01 | Result of divider is the routed to the output registers |
10 | Result of adder is the routed to the output registers |
11 | 1.0 is routed to the output registers |
GABE Registers: FP Status Registers ($AF:E204 – $AF:E207)
These registers contain the status flags for the various functional units. The status flags are all roughly similar across the four units:
Code | Meaning |
---|---|
$01 | Result was not a number (NAN) |
$02 | Overflow |
$04 | Underflow |
$08 | Result was 0.0 |
$10 | Division by 0.0 was attempted |
GABE Timer Registers ($00:0160 – $00:017F)
- Timer0 and Timer1 count system clock transitions (14318180Hz)
- Timer2 is clocked from VICKY's Start-of-Frame output (50hz).
- To create interrupts at a set interval for timer 0 and 1 divide the clock frequency by the desired interval. So if one wishes 4 interrupts every second use 14318180 divided by 4.
Start Address | End Address | Register Name | Additional Info |
---|---|---|---|
Timer 0 | |||
$00:0160 | TIMER0_CTRL_REG | Timer 0 control register | |
$00:0161 | TIMER0_CHARGE_L | Timer 0 precharge register lower byte, loaded when counting down | |
$00:0162 | TIMER0_CHARGE_M | Timer 0 precharge register middle byte, loaded when counting down | |
$00:0163 | TIMER0_CHARGE_H | Timer 0 precharge register high byte, loaded when counting down | |
$00:0164 | TIMER0_CMP_REG | Timer 0 compare register | |
$00:0165 | TIMER0_CMP_L | Timer 0 compare register lower byte, loaded when counting up | |
$00:0166 | TIMER0_CMP_M | Timer 0 compare register middle byte, loaded when counting up | |
$00:0167 | TIMER0_CMP_H | Timer 0 compare register high byte, loaded when counting up | |
Timer 1 | |||
$00:0168 | TIMER1_CTRL_REG | Timer 1 control register | |
$00:0169 | TIMER1_CHARGE_L | Timer 1 precharge register lower byte, loaded when counting down | |
$00:016A | TIMER1_CHARGE_M | Timer 1 precharge register middle byte, loaded when counting down | |
$00:016B | TIMER1_CHARGE_H | Timer 1 precharge register high byte, loaded when counting down | |
$00:016C | TIMER1_CMP_REG | Timer 1 compare register | |
$00:016D | TIMER1_CMP_L | Timer 1 compare register lower byte, loaded when counting up | |
$00:016E | TIMER1_CMP_M | Timer 1 compare register middle byte, loaded when counting up | |
$00:016F | TIMER1_CMP_H | Timer 1 compare register high byte, loaded when counting up | |
Timer 2 | |||
$00:0170 | TIMER2_CTRL_REG | Timer 2 control register | |
$00:0171 | TIMER2_CHARGE_L | Timer 2 precharge register lower byte, loaded when counting down | |
$00:0172 | TIMER2_CHARGE_M | Timer 2 precharge register middle byte, loaded when counting down | |
$00:0173 | TIMER2_CHARGE_H | Timer 2 precharge register high byte, loaded when counting down | |
$00:0174 | TIMER2_CMP_REG | Timer 2 compare register | |
$00:0175 | TIMER2_CMP_L | Timer 2 compare register lower byte, loaded when counting up | |
$00:0176 | TIMER2_CMP_M | Timer 2 compare register middle byte, loaded when counting up | |
$00:0177 | TIMER2_CMP_H | Timer 2 compare register high byte, loaded when counting up |
Register | Value | Name | Additional Info |
---|---|---|---|
TIMER<X>_CTRL_REG | 0x01 | TMR_EN | Enabled the timer |
TIMER<X>_CTRL_REG | 0x02 | TMR_SCLR | Set when counting up |
TIMER<X>_CTRL_REG | 0x04 | TMR_SLOAD | Set when counting down |
TIMER<X>_CTRL_REG | 0x08 | TMR_UPDWN | Set when counting up (1 = Up, 0 = Down) |
TIMER<X>_CMP_REG | 0x01 | TMR_CMP_RECLR | Set to one for it to cycle when Counting up |
TIMER<X>_CMP_REG | 0x02 | TMR_CMP_RELOAD | Set to one for it to reload when Counting Down |
C pseudo code for counting up
TIMER1_CHARGE_L = 0x00;
TIMER1_CHARGE_M = 0x00;
TIMER1_CHARGE_H = 0x00;
TIMER1_CMP_L = clockValue & 0xFF;
TIMER1_CMP_M = (clockValue >> 8) & 0xFF;
TIMER1_CMP_H = (clockValue >> 16) & 0xFF;
TIMER1_CMP_REG = TMR_CMP_RECLR;
TIMER1_CTRL_REG = TMR_EN | TMR_UPDWN | TMR_SCLR;
C pseudo code for counting down
TIMER1_CHARGE_L = (clockValue >> 0) & 0xFF;
TIMER1_CHARGE_M = (clockValue >> 8) & 0xFF;
TIMER1_CHARGE_H = (clockValue >> 16) & 0xFF;
TIMER1_CMP_L = 0x00;
TIMER1_CMP_M = 0x00;
TIMER1_CMP_H = 0x00;
TIMER1_CMP_REG = TMR_CMP_RELOAD;
TIMER1_CTRL_REG = TMR_EN | TMR_SLOAD;
GABE Interrupt Control Registers ($00:0140 – $00:014B)
There are four types of interrupt control register that GABE provides: pending, polarity, edge detection, and mask. Each interrupt that is supported has a bit position in each of the 24 or 32 bits provided by the register types.
- Pending
- The pending registers indicate if an interrupt of a particular type has been triggered and needs processing. An interrupt handler should also write to this register to clear the pending flag, once the interrupt has been processed.
- Polarity
- This register indicates if the interrupt is triggered by a high or low signal on the input to GABE.
- Edge
- This register indicates if the interrupt is triggered by an transition (edge) or by a high or low value.
- Mask
- This register indicates if the associated interrupt will trigger an IRQ to the processor. Interrupt signals with a mask bit of 0 will be ignored, while those with a mask bit of 1 will trigger an interrupt to the CPU.
Start | Register Description | Additional Info |
---|---|---|
$00:0140 | INT_PENDING_REG0 | Interrupt pending #0 |
$00:0141 | INT_PENDING_REG1 | Interrupt pending #1 |
$00:0142 | INT_PENDING_REG2 | Interrupt pending #2 |
$00:0143 | INT_PENDING_REG3 | Interrupt pending #3---FMX Model only |
$00:0144 | INT_POL_REG0 | Interrupt polarity #0 |
$00:0145 | INT_POL_REG1 | Interrupt polarity #1 |
$00:0146 | INT_POL_REG2 | Interrupt polarity #2 |
$00:0147 | INT_POL_REG3 | Interrupt polarity #3---FMX Model only |
$00:0148 | INT_EDGE_REG0 | Enable Edge Detection #0 |
$00:0149 | INT_EDGE_REG1 | Enable Edge Detection #1 |
$00:014A | INT_EDGE_REG2 | Enable Edge Detection #2 |
$00:014B | INT_EDGE_REG3 | Enable Edge Detection #3---FMX Model only |
$00:0148 | INT_MASK_REG0 | Enable Interrupt #0 |
$00:0149 | INT_MASK_REG1 | Enable Interrupt #1 |
$00:014A | INT_MASK_REG2 | Enable Interrupt #2 |
$00:014B | INT_MASK_REG3 | Enable Interrupt #3---FMX Model only |
There are several interrupts the GABE can handle, distributed over four blocks:
Block | Bit | Name | Function |
---|---|---|---|
0 | $01 | FNX0_INT00_SOF | Start of Frame @60Hz |
0 | $02 | FNX0_INT01_SOL | Start of Line (programmable) |
0 | $04 | FNX0_INT02_TMR0 | Timer 0 |
0 | $08 | FNX0_INT03_TMR1 | Timer 1 |
0 | $10 | FNX0_INT04_TMR2 | Timer 2 |
0 | $20 | FNX0_INT05_RTC | Real Time Clock |
0 | $40 | FNX0_INT06_FCC | Floppy Drive Controller |
0 | $80 | FNX0_INT07_MOUSE | Mouse Interrupt (INT12 in SuperIO IOspace) |
1 | $01 | FNX1_INT00_KBD | Keyboard Interrupt |
1 | $02 | FNX1_INT01_SC0 | VICKY_II (INT2) Sprite 2 Sprite Collision |
1 | $04 | FNX1_INT02_SC1 | VICKY_II (INT3) Sprite 2 Tiles Collision |
1 | $08 | FNX1_INT03_COM2 | Serial Port 2 (Internal) |
1 | $10 | FNX1_INT04_COM1 | Serial Port 1 (External) |
1 | $20 | FNX1_INT05_MPU401 | Midi Controller Interrupt |
1 | $40 | FNX1_INT06_LPT | Parallel Port |
1 | $80 | FNX1_INT07_SDCARD | SD Card Controller Interrupt (CH376S, if present) |
2 | $01 | FNX2_INT00_OPL3 | OPL3 |
2 | $02 | FNX2_INT01_GABE_INT0 | GABE (INT0) - TBD |
2 | $04 | FNX2_INT02_GABE_INT1 | GABE (INT1) - TBD |
2 | $08 | FNX2_INT03_SDMA | VICKY_II (INT4) |
2 | $10 | FNX2_INT04_VDMA | VICKY_II (INT5) -- Vicky DMA completion |
2 | $20 | FNX2_INT05_GABE_INT2 | GABE (INT2) - TBD |
2 | $40 | FNX2_INT06_EXT | External Expansion |
2 | $80 | FNX2_INT07_SDCARD_INS | SDCARD Insertion |
3 | $01 | FNX3_INT00_OPN2 | OPN2 |
3 | $02 | FNX3_INT01_OPM | OPM |
3 | $04 | FNX3_INT02_IDE | HDD IDE Interrupt |
3 | $08 | FNX3_INT03_TBD | TBD |
3 | $10 | FNX3_INT04_TBD | TBD |
3 | $20 | FNX3_INT05_TBD | TBD |
3 | $40 | FNX3_INT06_TBD | TBD |
3 | $80 | FNX3_INT07_TBD | TBD |
GABE SID Emulation ($AF:E400 – $AF:E41F)
GABE emulates the SID sound chip. Currently, the FMX provides one SID unit. Although the real SID chip provides analog to digital conversion for two potentiometer inputs for paddles, the C256's paddle inputs are provided through a different chip, so the POT registers listed below are not useful.
For complete information on how to use these registers, please consult documentation on the SID chips.
All registers are write-only, except where noted otherwise.
Start Address | End Address | Register Name | Additional Information |
---|---|---|---|
$AF:E400 | $AF:E401 | SID0_V1_FREQ | SID0 V1 Frequency (16-bits) |
$AF:E402 | $AF:E403 | SID0_V1_PW | SID0 V1 Pulse Width (12-bits) |
$AF:E404 | SID0_V1_CTRL | SID0 V1 Control (8-bits) | |
$AF:E405 | SID0_V1_ATCK_DECY | SID0 V1 Attack/Decay (8-bits) | |
$AF:E406 | SID0_V1_SSTN_RLSE | SID0 V1 Sustain/Release (8-bits) | |
$AF:E407 | $AF:E408 | SID0_V2_FREQ | SID0 V2 Frequency (16-bits) |
$AF:E409 | $AF:E40A | SID0_V2_PW | SID0 V2 Pulse Width (12-bits) |
$AF:E40B | SID0_V2_CTRL | SID0 V2 Control (8-bits) | |
$AF:E40C | SID0_V2_ATCK_DECY | SID0 V2 Attack/Decay (8-bits) | |
$AF:E40D | SID0_V2_SSTN_RLSE | SID0 V2 Sustain/Release (8-bits) | |
$AF:E40E | $AF:E40F | SID0_V3_FREQ | SID0 V3 Frequency (16-bits) |
$AF:E410 | $AF:E411 | SID0_V3_PW | SID0 V3 Pulse Width (12-bits) |
$AF:E412 | SID0_V3_CTRL | SID0 V3 Control (8-bits) | |
$AF:E413 | SID0_V3_ATCK_DECY | SID0 V3 Attack/Decay (8-bits) | |
$AF:E414 | SID0_V3_SSTN_RLSE | SID0 V3 Sustain/Release (8-bits) | |
$AF:E415 | SID0_FC_LO | SID0 Filter Frequency LOW (3 bits) | |
$AF:E416 | SID0_FC_HI | SID0 Filter Frequency HIGH (8 bits) | |
$AF:E417 | SID0_RES_FILT | SID0 Resonance / Filter (8-bits) | |
$AF:E418 | SID0_MODE_VOL | SID0 Mode / Volume (8-bits) | |
$AF:E419 | SID0_POT_X | SID0 POT X (Read only, C256 - NOT USED) | |
$AF:E41A | SID0_POT_Y | SID0 POT Y (Read only, C256 - NOT USED) | |
$AF:E41B | SID0_OSC3_RND | SID0 OSC3 / RANDOM (Read only, 8-bits) | |
$AF:E41C | SID0_ENV3 | SID0 ENV3 (Read only, 8-bits) |
GABE Trinity Joystick/Gamepad and DIP Switch Registers ($AF:E800 – $AF:E80E)
GABE provides access to the Trinity chip, which provides access to the joystick ports and the DIP switches. The joystick ports can be used in two modes: default mode, where they work like Atari style joysticks, and NES mode where they can interface with either NES or SNES game pads.
Address | Name | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Description |
---|---|---|---|---|---|---|---|---|---|---|
$AF:E800 | JOYSTICK0 | BUTTON2 | BUTTON1 | 0 | BUTTON0 | RIGHT | LEFT | DOWN | UP | Left-most joystick port |
$AF:E801 | JOYSTICK1 | BUTTON2 | BUTTON1 | 0 | BUTTON0 | RIGHT | LEFT | DOWN | UP | Next to joystick port 0 |
$AF:E802 | JOYSTICK2 | 0 | 0 | 0 | BUTTON0 | RIGHT | LEFT | DOWN | UP | Next to joystick port 1 |
$AF:E803 | JOYSTICK3 | 0 | 0 | 0 | BUTTON0 | RIGHT | LEFT | DOWN | UP | Right-most joystick port |
$AF:E804 | JOYSTICK_MODE | NES_TRIG | 0 | 0 | 0 | NES_DONE | NES_JOY | NES_EN1 | NES_EN0 | Control SNES compatibility |
$AF:E805 | REVOFPCB_C | "C" | Board identification code byte (expect "C") | |||||||
$AF:E806 | REVOFPCB_4 | "4" | Board identification code byte (expect "4") | |||||||
$AF:E807 | REVOFPCB_A | "A" | Board identification code byte (expect "A") | |||||||
$AF:E808 | NES_SNES0_DAT_LO | see below | Port 0: NES or SNES gamepad data (low byte) | |||||||
$AF:E809 | SNES0_DAT_HI | see below | Port 0: NES or SNES gamepad data (highbyte) | |||||||
$AF:E80A | NES_SNES1_DAT_LO | see below | Port 1: NES or SNES gamepad data (low byte) | |||||||
$AF:E80B | SNES1_DAT_HI | see below | Port 1: NES or SNES gamepad data (highbyte) | |||||||
$AF:E80C | CFP9301_REV | revision | Trinity revision code | |||||||
$AF:E80D | DIP_USER | user | Switches 3 through 5. User defined meaning. | |||||||
$AF:E80E | DIP_BOOTMODE | HD_INSTALLED | 0 | 0 | 0 | 0 | 0 | BM1 | BM0 | Boot control switches. |
Address | Name | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Description |
---|---|---|---|---|---|---|---|---|---|---|
$AF:E808 | NES_SNES0_DAT_LO | B | Y | SELECT | START | UP | DOWN | LEFT | RIGHT | NES data if NES_JOY = 1 and NES_EN0 = 1 |
$AF:E809 | SNES0_DAT_HI | 0 | 0 | 0 | 0 | A | X | L | R | NES data if NES_JOY = 1 and NES_EN0 = 1) |
$AF:E80A | NES_SNES1_DAT_LO | B | Y | SELECT | START | UP | DOWN | LEFT | RIGHT | NES data if NES_JOY = 1 and NES_EN1 = 1 |
$AF:E80B | SNES1_DAT_HI | 0 | 0 | 0 | 0 | A | X | L | R | NES data if NES_JOY = 1 and NES_EN1 = 1 |
Address | Name | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Description |
---|---|---|---|---|---|---|---|---|---|---|
$AF:E808 | NES_SNES0_DAT_LO | A | B | SELECT | START | UP | DOWN | LEFT | RIGHT | NES data if NES_JOY = 0 and NES_EN0 = 1 |
$AF:E809 | SNES0_DAT_HI | N/A | Unused if NES_EN0 = 0 | |||||||
$AF:E80A | NES_SNES1_DAT_LO | A | B | SELECT | START | UP | DOWN | LEFT | RIGHT | NES data if NES_JOY = 0 and NES_EN1 = 1 |
$AF:E80B | SNES1_DAT_HI | N/A | Unused in NES mode |
NES Control Bits
- NES_TRIG
- Set to one to trigger the serializer to read the gamepad.
- NES_DONE
- Poll to see if the serializer has finished reading the gamepad.
- NES_JOY
- If 1, sets SNES compatibility. If 0, sets NES compatibility.
- NES_EN1
- If 1, enables NES/SNES compatibility on port 1. If 0, joysticks work like Atari joysticks
- NES_EN0
- If 1, enables NES/SNES compatibility on port 0. If 0, joysticks work like Atari joysticks
Boot Mode
Switches 0 and 1 allow you to select the boot mode for the FMX. Note that the switches are active low: a switch in the "On" position registers as a 0.
BM1 | BM0 | Boot Mode |
---|---|---|
0 | 0 | Boots from the IDE drive DIP switch 8 must be set (HD_INSTALLED). |
0 | 1 | Boots from the SD card |
1 | 0 | Boots from the floppy drive |
1 | 1 | Boots to BASIC |
GABE IDE Registers ($AF:E830 – $AF:E839)
GABE provides an IDE interface to a hard drive (if installed). This is done through the Unity chip and uses standard IDE interface command, error, and status codes.
Address | R/W | Name | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Description |
---|---|---|---|---|---|---|---|---|---|---|---|
$AF:E830 | x | IDE_DATA | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | 8-bit data read/write |
$AF:E831 | R | IDE_ERROR | BBK | UNC | MC | IDNF | MCR | ABRT | TK0NF | AMNF | Error code for the transaction |
$AF:E832 | W | IDE_SECT_CNT | SC7 | SC6 | SC5 | SC4 | SC3 | SC2 | SC1 | SC0 | Sector count |
$AF:E833 | W | IDE_SECT_SRT | SS7 | SS6 | SS5 | SS4 | SS3 | SS2 | SS1 | SS0 | Sector start (0 = 256), start at 1 |
$AF:E834 | W | IDE_CLDR_LO | C7 | C6 | C5 | C4 | C3 | C2 | C1 | C0 | Cylinder Number (low) |
$AF:E835 | W | IDE_CLDR_HI | C15 | C14 | C13 | C12 | C11 | C10 | C9 | C8 | Cylinder Number (high) |
$AF:E836 | W | IDE_HEAD | 1 | 0 | 1 | M/S | H3 | H2 | H1 | H0 | Head number and Master (0) / Slave(1) device |
$AF:E837 | W | IDE_CMD_STAT | CMD7 | CMD6 | CMD5 | CMD4 | CMD3 | CMD2 | CMD1 | CMD0 | Send a command to the drive |
$AF:E837 | R | IDE_CMD_STAT | BSY | DRDY | DF | DSC | DRQ | CORR | IDX | ERR | Get the status of device. Reading this will clear the Interrupt Registers |
$AF:E838 | X | IDE_DATA_LO | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | 16-bit data transfer registers (low byte) |
$AF:E839 | X | IDE_DATA_HI | D15 | D14 | D13 | D12 | D11 | D10 | D9 | D8 | 16-bit data transfer registers (high byte) |
GABE IDE Status Flags
- BSY
- Device is busy if set
- DRDY
- Device is ready for a command if set
- DF
- Device fault
- DSC
- Device seek complete. If set, the head has stopped moving and is over a track.
- DRQ
- Device is ready to transfer a data byte (either read or write) if set.
- CORR
- If set, indicates that data was corrected automatically.
- IDX
- A vendor specific status
- ERR
- If set, an error occurred with additional data in the error register.
GABE IDE Error Flags
- BBK
- Bad block
- UNC
- Uncorrectable data error
- MC
- Media changed
- IDNF
- ID mark not found
- MCR
- Media change requested
- ABRT
- Command aborted
- TK0NF
- Track 0 not found
- AMNF
- Address mark not found
GABE Control Registers ($AF:E880 – $AF:E887)
Start Address | Ending Address | Register Description | Additional Info |
---|---|---|---|
$AF:E880 | GABE_MSTR_CTRL | ||
$AF:E881 | Reserved | ||
$AF:E882 | GABE_RST_AUTH0 | Must Contain the BYTE $AD for Reset to Activate | |
$AF:E883 | GABE_RST_AUTH1 | Must Contain the BYTE $DE for Reset to Activate | |
$AF:E884 | $AF:E885 | GABE_RNG_DATASEED | On read: 16-bit random data. On write, set 16-bit RNG seed. |
$AF:E886 | GABE_RNG_STATCTRL | On read: 8-bit status. On write: 8-bit control | |
$AF:E887 | GABE_SYS_STAT | 8-bit system status |
GABE Master Control Register ($AF:E880)
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
GABE_CTRL_WRM_RST | GABE_CTRL_BUZZER | GABE_CTRL_SDC_LED | GABE_CTRL_PWR_LED |
- GABE_CTRL_PWR_LED
- Turns the power LED (next to the reset button) on or off.
- GABE_CTRL_SDC_LED
- Turns the SDC activity LED (next to the SDC slot) on or off.
- GABE_CTRL_BUZZER
- Turns the built-in piezo buzzer on or off.
- GABE_CTRL_WRM_RST
- Triggers a warm reset of the board (GABE_RST_AUTH0 must be set to $AD and GABE_RST_AUTH1 to $DE in order to trigger the reset).
GABE Random Number Generator Status and Control ($AF:E886)
R/W | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
R | GABE_RNG_LFSR_DONE | |||||||
W | GABE_RNG_CTRL_DV | GABE_RNG_CTRL_EN |
- GABE_RNG_LFSR_DONE
- Indicates that output = seed database.
- GABE_RNG_CTRL_DV
- After setting the seed value, toggle this bit for the seed be registered.
- GABE_RNG_CTRL_EN
- Enable the LFSR block.
GABE System Status Register ($AF:E887)
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
GABE_SYS_STAT_CPUX | GABE_SYS_STAT_CPUA | GABE_SYS_STAT_EXP | GABE_SYS_STAT_MID1 | GABE_SYS_STAT_MID0 |
- GABE_SYS_STAT_CPUX
- Indicates if the CPU's index registers are 8-bits or 16-bits wide.
- GABE_SYS_STAT_CPUA
- Indicates if the CPU's accumulator is 8-bits or 16-bits wide.
- GABE_SYS_STAT_EXP
- Indicates if the and expansion card is present (0).
- GABE_SYS_STAT_MID1 and GABE_SYS_STAT_MID0
- These two bits show the machine ID:
GABE_SYS_STAT_MID1 | GABE_SYS_STAT_MID0 | Machine |
---|---|---|
0 | 0 | FMX - Development Platform |
0 | 1 | C256 Foenix - Dev Platform |
1 | 0 | C256 Foenix - User Version (65C816) |
1 | 1 | Reserved |
GABE SDC Interface ($AF:EA00)
GABE provides block-level access to the SPI interface of the SD card. Currently, there are other registers in that provide access to the SD card through a CH376S chip, but that interface should be considered deprecated in favor of this interface. Most FMX boards do not have the CH376S chip, leaving its position unpopulated on the board.
Address | R/W | Name | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Description |
---|---|---|---|---|---|---|---|---|---|---|---|
$AF:EA00 | R | SDC_VERSION_REG | version | Should read 12 | |||||||
$AF:EA01 | W | SDC_CONTROL_REG | x | x | x | x | x | x | x | RESET | Control register: RESET = 1 resets the core logic. Self clearing. |
$AF:EA02 | W | SDC_TRANS_TYPE_REG | x | x | x | x | x | x | TRANS | Sets the type of transaction: 0 = Direct, 1 = INIT SD, 2 = Read Block, 3 = Write block | |
$AF:EA03 | W | SDC_TRANS_CONTROL_REG | x | x | x | x | x | x | x | START | Start transaction bit. |
$AF:EA04 | R | SDC_TRANS_STATUS_REG | x | x | x | x | x | x | x | BUSY | Status of the transaction. BUSY = 1 indicates a transaction is in progress. |
$AF:EA05 | R | SDC_TRANS_ERROR_REG | x | x | WR_ERR | RD_ERR | TRANS_ERR | Error conditions for read, write, and transaction. | |||
$AF:EA06 | RW | SDC_DIRECT_ACCESS_REG | data | SPI Direct Read and Write - Set DATA before initiating direct Access Transaction | |||||||
$AF:EA07 | W | SDC_SD_ADDR_7_0_REG | A7 | A6 | A5 | A4 | A3 | A2 | A1 | A0 | Set the ADDR before a block read or block write. |
$AF:EA08 | W | SDC_SD_ADDR_15_8_REG | A15 | A14 | A13 | A12 | A11 | A10 | A9 | A8 | ADDR[8:0] always should be 0, since each block is 512 bytes |
$AF:EA09 | W | SDC_SD_ADDR_23_16_REG | A23 | A22 | A21 | A20 | A19 | A18 | A7 | A6 | |
$AF:EA0A | W | SDC_SD_ADDR_31_24_REG | A32 | A31 | A30 | A29 | A28 | A26 | A25 | A24 | |
$AF:EA0B | W | SDC_SPI_CLK_DEL_REG | clock delay | ||||||||
$AF:EA10 | R | SDC_RX_FIFO_DATA_REG | data | Data from block read | |||||||
$AF:EA12 | R | SDC_RX_FIFO_DATA_CNT_HI | CNT15 | CNT14 | CNT13 | CNT12 | CNT11 | CNT10 | CNT9 | CNT8 | How many bytes in the read FIFO (HI) |
$AF:EA13 | R | SDC_RX_FIFO_DATA_CNT_LO | CNT7 | CNT6 | CNT5 | CNT4 | CNT3 | CNT2 | CNT1 | CNT0 | How many bytes in the read FIFO (LOW) |
$AF:EA14 | W | SDC_RX_FIFO_CTRL_REG | x | x | x | x | x | x | x | CLEAR | CLEAR = 1, clear the read FIFO. Bit is self-clearing. |
$AF:EA20 | W | SDC_TX_FIFO_DATA_REG | data | Write data block here. | |||||||
$AF:EA21 | W | SDC_TX_FIFO_CTRL_REG | x | x | x | x | x | x | x | CLEAR | CLEAR = 1, clear the write FIFO. Bit is self-clearing. |
GABE: SDC_TRANS_ERROR_REG ($AF:EA05)
Lists any error conditions on reads, writes, or general transactions:
Code | Condition |
---|---|
00 | No INIT error |
01 | INIT CMD0 error |
10 | INIT CMD1 error |
Code | Condition |
---|---|
00 | No read error |
01 | CMD error |
10 | Token error |
Code | Condition |
---|---|
00 | No write error |
01 | CMD error |
10 | Data error |
11 | Busy error |
GABE SN76489 Interface ($AF:F100)
GABE provides the interface to the SN76489 sound chip on the Foenix FMX. This chip uses only a single write-only address: $AF:F100. The SN76489 has four voices: three tone generators, and one noise generator. Each voice has a frequency and an attenuation setting. For full details on how to use the SN76489 chip, please consult the official documentation, but here is a brief summary.
Playing a Tone
To change the frequency of a tone generator, two bytes are sent to the chip in order:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|
First | 1 | Channel | 0 | F3 | F2 | F1 | F0 | |
Second | 0 | x | F9 | F8 | F7 | F6 | F5 | F4 |
The actual frequency played depends on the clock driving the chip: freq = N / (32 * F), where N = 3575800 (for the FMX), and F is the 10-bit number sent to the chip. The "Channel" number above must be 0, 1, or 3.
Play a Noise
To play a noise, only a single byte is written to the chip:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
1 | 1 | 1 | 0 | x | NF | NF1 | NF0 |
The NF bit controls the type of noise generated, and the NF bits control the base frequency of the noise.
Setting the Volume
The volumes of each of the four voices can be set independently. To set the volume of a single voice, a single byte is sent to the chip:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
1 | Channel | 1 | A3 | A2 | A1 | A0 |
The "Channel" bits are the number of the voice to change: 0, 1, 2, or 3. The four bit value A is the level of attenuation, where 0 is fully on, and 15 is fully off.