# Custom Chips {{:amiga:all_custom_chips_working_web.png}} ## Specific Chips * [[amiga:custom_chips:agnus|Agnus]] * [[amiga:custom_chips:agnus:blitter|Amiga Agnus Blitter]] * [[amiga:custom_chips:agnus:copper|Amiga Agnus Copper Co-Processor]] * [[amiga:custom_chips:denise|Denise]] ## Getting ready to hit the hardware directly Picked apart from http://stingray.untergrund.net/MiniStartup.s * http://amigadev.elowar.com/read/ADCD_2.1/Includes_and_Autodocs_2._guide/node009E.html * https://somuch.guru/2014/11/02/hrtmon-updating-nmi-vector/ * https://www.markwrobel.dk/post/amiga-machine-code-letter9/ ```mermaid graph TD open_gfx[Open the Graphics library] get_current_view[Get the current ActiView] load_view_null[LoadView NULL] waittof_twice[WaitTOF twice] store_old_copperlists[Store old Copperlists] get_cpu_type[Get CPU Type] 68010_or_above{68010 or above?} get_010_vector_base_register[Get 010 Vector Base Register] return_0_for_vector_base_register[Return 0 for Vector Base Register] store_old_registers[Store old custom chip registers -- ADKCON, INTENA, DMA] wait_for_raster[Wait for end of raster beam] disable_and_clear_registers[Disable and clear registers -- Interrupts, DMA, INTREQ] install_custom_vertical_blank_interrupt_handler[Install custom vertical blank interrupt handler] set_up_new_dma_interrupts[Set up new DMA and interrupts] open_gfx --> get_current_view --> load_view_null --> waittof_twice waittof_twice --> store_old_copperlists --> get_cpu_type --> 68010_or_above 68010_or_above -- yes --> get_010_vector_base_register 68010_or_above -- no --> return_0_for_vector_base_register get_010_vector_base_register --> store_old_registers return_0_for_vector_base_register --> store_old_registers --> wait_for_raster --> disable_and_clear_registers --> install_custom_vertical_blank_interrupt_handler --> set_up_new_dma_interrupts ``` ```mermaid clear_custom_vertical_blank_interrupt_store[Clear custom vertical blank interrupt store] wait_for_raster[Wait for raster] restore_interrupts_dma[Restore Interrupts and DMA] restore_vertical_blank_interrupt_handler[Restore vertical blank interrupt handler] restore_copperlists[Restore copperlists] start_old_copperlist[Start old copperlist] restore_registers[Restore registers -- Interrupts, DMA, ADK] restore_viewport[Restore viewport] close_graphics[Close graphics library] ``` You need to own the machine if you want to hit the custom chips directly. This requires: * Preserving a bunch of registers * Capturing the current View and Copperlist * Destroying the view * Waiting two vertical blanks for any existing copperlists to complete * Taking ownership of the blitter and waiting for it to finish * Forbidding all other tasks from running * ...though you might not want to do this if you want to use things that use interrupts! ### Assembler ```asm OpenLibrary MACRO MOVE.L $4,A6 MOVE.L #\1,A1 MOVE.L #\2,D0 CALLLIB _LVOOpenLibrary ENDM INCLUDE "graphics/gfxbase.i" INCLUDE "graphics_lvo.i" ; requires fd2pragma to create INCLUDE "exec/exec_lib.i" ; preserve registers code is below PreserveViewAndCopperList: OpenLibrary GraphicsLibrary,0 MOVE.L D0,GraphicsBase MOVE.L D0,A6 MOVE.L gb_ActiView(A6),OldView MOVE.L gb_copinit(A6),OldCopper TakeOwnership: MOVE.L #0,A1 CALLLIB _LVOLoadView ; nuke the view CALLLIB _LVOWaitTOF ; wait two vertical blanks CALLLIB _LVOWaitTOF CALLLIB _LVOOwnBlitter CALLLIB _LVOWaitBlit ; wait... MOVE.L $4,A6 CALLLIB _LVOForbid ; the system is ours CNOP 0,4 OldView dc.l 0 OldCopper dc.l 0 GraphicsBase dc.l 0 CNOP 0,4 GraphicsLibrary GRAPHICSNAME ``` ### C Based on https://github.com/weiju/amiga_hardware_in_c/blob/master/episode-004/example_04.c ```c #include #include #include #include #include extern struct GfxBase *GfxBase; extern far struct Custom custom; int main(void) { struct View *OldView = ((struct GfxBase *)GfxBase)->ActiView; ULONG OldCopper = (ULONG)((struct GfxBase *)GfxBase)->copinit; LoadView(NULL); WaitTOF(); WaitTOF(); OwnBlitter(); WaitBlit(); Forbid(); } ``` To undo this: * Restore the old registers * Replace the old copperlist * Restore the old view * Wait for our copperlist and blitter to finish * Disown the blitter * Permit the rest of the OS from running ```asm ReturnOwnership: RestoreRegister DMACON RestoreRegister INTENA RestoreRegister INTREQ RestoreRegister ADKCON MOVE.L OldCopper,COP1LC MOVE.L GraphicsBase,A6 MOVE.L OldView,A1 CALLLIB _LVOLoadView ; restore the view CALLLIB _LVOWaitTOF CALLLIB _LVOWaitTOF CALLLIB _LVOWaitBlit CALLLIB _LVODisownBlitter MOVE.L $4,A6 CALLLIB _LVOPermit ``` ```c int main(void) { custom.cop1lc = OldCopper; LoadView(OldView); WaitTOF(); WaitTOF(); WaitBlit(); DisownBlitter(); Permit(); } ``` ## Registers * http://www.amigadev.elowar.com/read/ADCD_2.1/Hardware_Manual_guide/node0060.hml ## Reading/Writing Registers If you're preserving registers: * Capture the original value from the read register * OR that value with $8000 * When you're ready to put the data back, write $7fff to clear the contents of the register * Setting bit 15 to 0 means any other bit set to 1 clears that bit rather than sets it * Write back your original value This is done with: * DMACON/DMACONR * INTENA/INTENAR * INTREQ.INTREQR * ADKCON/ADKCONR ```asm PreserveRegister MACRO MOVE.W \1R,D0 OR.W #$8000,D0 MOVE.W D0,Old\1 ENDM RestoreRegister MACRO MOVE.W #$7FFF,\1 MOVE.W Old\1,\1 ENDM PreserveRegisters: PreserveRegister DMACON PreserveRegister INTENA PreserveRegister INTREQ PreserveRegister ADKCON ReturnOwnership: RestoreRegister DMACON RestoreRegister INTENA RestoreRegister INTREQ RestoreRegister ADKCON CNOP 0,4 OldDMACON dc.w 0 OldINTENA dc.w 0 OldINTREQ dc.w 0 OldADKCON dc.w 0 ``` ```c int main(void) { UWORD OldDMACON,OldINTENA,OldINTREQ,OldADKCON; OldDMACON = custom.dmaconr | 0x8000; OldINTENA = custom.intenar | 0x8000; OldINTREQ = custom.intreqr | 0x8000; OldADKCON = custom.adkconr | 0x8000; custom.dmacon = 0x7FFF; custom.dmacon = OldDMACON; custom.intena = 0x7FFF; custom.intena = OldINTENA; custom.intreq = 0x7FFF; custom.intreq = OldINTREQ; custom.adkcon = 0x7FFF; custom.adkcon = OldADKCON; } ``` #### Horizontal scroll Use BPLCON1. ## Agnus [[amiga:custom_chips:agnus|Agnus Amiga Custom Chip]] ## Denise [[amiga:custom_chips:denise|Denise Amiga Custom Chip]] ## Interrupts ### Examples #### Disable all interrupts ```asm INTENA.MASTER EQU %0100000000000000 MOVE.W #%1100000000000000,INTENA MOVE.W #%0011111111111111,INTENA ``` ```c custom.intena = 0xc000; custom.intena = 0x3fff; ``` ## Other Chips ### 8520 CIA chips * http://www.amigadev.elowar.com/read/ADCD_2.1/Hardware_Manual_guide/node012E.html `hardware/cia.i` will provide addresses and constants for working with the CIA chips. #### Reading the left mouse button or joystick button on either port Buttons are 1 when up and 0 when pushed. ```asm CIAAPRA EQU $BFE001 Port1Fire EQU 6 Port2Fire EQU 7 WaitForMouse: BTST.B #Port1Fire,CIAAPRA BEQ Done BTST #Port2Fire,CIAAPRA BEQ Done BRA WaitForMouse Done: ``` ```c #include extern struct CIA far ciaa,ciab; int main(void) { while ((ciaa.ciaapra >> 6) == 3) {} // both buttons are up. } ``` #### Reading the Keyboard * http://amigadev.elowar.com/read/ADCD_2.1/Hardware_Manual_guide/node013E.html * http://eab.abime.net/showpost.php?p=364323&postcount=7 * https://www.whdload.de/docs/en/rawkey.html * http://wiki.amigaspirit.hu/index.php/Amiga_Machine_Language_(Chapter_5) ```c volatile static char *ciaa_sdr = (volatile char*) 0xbfec01; // any key is down if (ciaa_sdr & 0x1) { printf("any key is down\n"); } ``` ##### Notes It doesn't seem possible to detect the following: * push key down * push another key down * pull up first key * pull up second key The CIA A SDR value will change to the second key + key down, then to first key + key up when you let go. There's no way to get the final second key up. ## AGA * https://raw.githubusercontent.com/rkrajnc/minimig-mist/master/doc/amiga/aga/AGA.guide