Custom Chips

Specific Chips

Getting ready to hit the hardware directly

Picked apart from http://stingray.untergrund.net/MiniStartup.s

snippet.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
snippet.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

snippet.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

snippet.c
#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <graphics/gfxbase.h>
#include <hardware/custom.h>
 
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
snippet.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
snippet.c
int main(void) {
  custom.cop1lc = OldCopper;
  LoadView(OldView);
  WaitTOF();
  WaitTOF();
  WaitBlit();
  DisownBlitter();
  Permit();
}

Registers

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
snippet.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
snippet.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

Denise

Interrupts

Examples

Disable all interrupts

snippet.asm
INTENA.MASTER EQU %0100000000000000
 
  MOVE.W #%1100000000000000,INTENA
  MOVE.W #%0011111111111111,INTENA
snippet.c
custom.intena = 0xc000;
custom.intena = 0x3fff;

Other Chips

8520 CIA chips

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.

snippet.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:
snippet.c
#include <hardware/cia.h>
 
extern struct CIA far ciaa,ciab;
 
int main(void) {
  while ((ciaa.ciaapra >> 6) == 3) {} // both buttons are up.
}

Reading the Keyboard

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