;************************************************************************* ; EOS-8 VRAM Routines for the Coleco ADAM ; ; Modified from the EOS-7 source code by Richard F. Drushel 9210.06 ; Implements maskable and non-maskable interrupt deferral to prevent ; reentrancy into code which accesses the TMS9928A VDP. ; ; Note: interrupts are deferred only one level deep! ; Note: code at INT: and NMI: must be loaded by the user application! ; Note: __PUT_ASCII and __PORT_COLLECTION cannot be protected against ; interrupts, because they involve bank switching to the OS-7 and ; SmartWriter ROMs, respectively, both of which lie in the low ; 32K of memory. Thus, interrupts *MUST* be disabled when these ; routines are CALLed, because the interrupt handler in RAM will ; be swapped out! ;************************************************************************* INTERRUPT_FLAGS EQU xxxxx ;some global location in EOS-8 RAM (new) ;other EOS globals at standard locations ;see EOS Technical Manual for values ORG 56 ;Z80 INT vector INT: A56: PUSH AF ;save register LD A,(INTERRUPT_FLAGS) ;point to interrupt flags BIT 0,A ;is a VRAM I/O in progress? JR Z,A71 ;NO, so just do the routine SET 2,A ;YES, so request a deferred INT LD (INTERRUPT_FLAGS),A ;save it back POP AF ;restore AF RET ;return with INTs *DISABLED* A71: RES 2,A ;clear request flag for safety LD (INTERRUPT_FLAGS),A ;save it CALL A83 ;do the user INT routine (doesn't need ;to PUSH AF) POP AF ;restore AF EI ;reenable INTs RETI ;return from maskable interrupt A83: JP user_int_routine ;jump to the actual user code ;it must save AF, HL and any other used ;registers, and end in RET ORG 102 ;Z80 NMI vector NMI: A102: PUSH AF ;save register LD A,(INTERRUPT_FLAGS) ;get interrupt flags BIT 0,A ;is a VRAM I/O in progress? JR Z,A117 ;NO, so just do the routine SET 1,A ;YES, so request a deferred NMI LD (INTERRUPT_FLAGS),A ;save it back POP AF ;restore AF RET ;return with NMIs *DISABLED* A117: RES 1,A ;clear request flag for safety LD (INTERRUPT_FLAGS),A ;and save it back CALL A133 ;do the user NMI routine (doesn't need ;to PUSH AF) IN A,(191) ;restart the NMI by reading VDP register 8 LD (VDP_STATUS_BYTE),A ;save it for EOS usage POP AF ;restore AF RETN ;return from non-maskable interrupt A133: JP user_nmi_routine ;jump to the actual user code ;it must save AF, HL and any other used ;registers, and end in RET ;end code that user application must install ;begin code that is in modified EOS ORG 0E000H ;start of EOS in RAM __WRITE_VRAM: PUSH HL ;save register LD HL,INTERRUPT_FLAGS ;point to interrupt flags BIT 0,(HL) ;is a VRAM I/O in progress? JR NZ,DO_WRITE_VRAM_2 ;YES, so just do it ;(interrupts are already deferred) SET 0,(HL) ;NO, so mark it now in progress POP HL ;restore HL CALL DO_WRITE_VRAM ;do the VRAM routine JP VRAM_COMMON_EXIT ;__WRITE_VRAM ;********** DO_WRITE_VRAM_2: POP HL DO_WRITE_VRAM: PUSH BC EX DE,HL CALL SET_WRITE LD L,C POP BC EX DE,HL LD A,C LD C,E LD D,B INC D LD B,A OR A JR Z,OUT_DEC_HI_BYTE OUTPUT_LOOP: OUTI NOP NOP JR NZ,OUTPUT_LOOP OUT_DEC_HI_BYTE: DEC D JR NZ,OUTPUT_LOOP RET ;************************************************************************** __READ_VRAM: PUSH HL ;save register LD HL,INTERRUPT_FLAGS ;point to interrupt flags BIT 0,(HL) ;is a VRAM I/O in progress? JR NZ,DO_READ_VRAM_2 ;YES, so just do it ;(interrupts are already deferred) SET 0,(HL) ;NO, so mark it now in progress POP HL ;restore HL CALL DO_READ_VRAM ;do the VRAM routine JR VRAM_COMMON_EXIT ;__READ_VRAM ;********** DO_READ_VRAM_2: POP HL DO_READ_VRAM: PUSH BC EX DE,HL CALL SET_READ LD L,C POP BC EX DE,HL LD A,C LD C,E LD D,B INC D LD B,A OR A JR Z,IN_DEC_HI_BYTE INPUT_LOOP: INI NOP NOP JR NZ,INPUT_LOOP IN_DEC_HI_BYTE: DEC D JR NZ,INPUT_LOOP RET ;***************************** __WRITE_REGISTER: PUSH HL ;save register LD HL,INTERRUPT_FLAGS ;point to interrupt flags BIT 0,(HL) ;is a VRAM I/O in progress? JR NZ,DO_WRITE_REGISTER_2 ;YES, so just do it ;(interrupts are already deferred) SET 0,(HL) ;NO, so mark it now in progress POP HL ;restore HL CALL DO_WRITE_REGISTER ;do the routine JR VRAM_COMMON_EXIT ;__WRITE_REGISTER ;********** DO_WRITE_REGISTER_2: POP HL DO_WRITE_REGISTER: LD E,C LD A,(VDP_CTRL_PORT) LD C,A OUT (C),E LD A,B OR 80H OUT (C),A LD A,B OR A LD A,E JR NZ,CHK_REG_1 LD (VDP_MODE_WORD),A RET CHK_REG_1: DEC B RET NZ LD (VDP_MODE_WORD+1),A RET ;***************************** __READ_REGISTER: PUSH HL ;save register LD HL,INTERRUPT_FLAGS ;point to interrupt flags BIT 0,(HL) ;is a VRAM I/O in progress? JR NZ,DO_READ_REGISTER_2 ;YES, so just do it ;(interrupts are already deferred) SET 0,(HL) ;NO, so mark it now in progress POP HL ;restore HL CALL DO_READ_REGISTER ;do the routine JR VRAM_COMMON_EXIT ;__READ_REGISTER ;********** DO_READ_REGISTER_2: POP HL DO_READ_REGISTER: LD A,(VDP_CTRL_PORT) LD C,A IN A,(C) LD (VDP_STATUS_BYTE),A RET ;***************************** __FILL_VRAM: PUSH HL ;save register LD HL,INTERRUPT_FLAGS ;point to interrupt flags BIT 0,(HL) ;is a VRAM I/O in progress? JR NZ,DO_FILL_VRAM_2 ;YES, so just do it ;(interrupts are already deferred) SET 0,(HL) ;NO, so mark it now in progress POP HL ;restore HL CALL DO_FILL_VRAM ;do the routine VRAM_COMMON_EXIT: PUSH HL ;save HL LD HL,INTERRUPT_FLAGS ;point to interrupt flags BIT 2,(HL) ;is there a deferred INT? JR NZ,DO_DEF_INT ;YES, so do it BIT 1,(HL) ;is there a deferred NMI? JR NZ,DO_DEF_NMI ;YES, so do it RES 0,(HL) ;NO, so no deferred interrupts; all done POP HL ;restore HL RET ;bye! DO_DEF_INT: RES 2,(HL) ;clear request flag CALL A83 ;do user INT routine RES 0,(HL) ;all done with VRAM routine POP HL ;restore HL EI ;finally reenable INTs RETI ;return from maskable interrupt DO_DEF_NMI: RES 1,(HL) ;clear request flag CALL A133 ;do user NMI routine RES 0,(HL) ;clear VRAM flag POP HL ;restore HL PUSH AF ;save AF IN A,(191) ;restart NMIs by reading VDP register 8 LD (VDP_STATUS_BYTE),A ;save it for EOS POP AF ;restore AF RETN ;return from non-maskable interrupt ;********** DO_FILL_VRAM_2: POP HL DO_FILL_VRAM: PUSH AF CALL SET_WRITE POP HL FILL: OUT (C),H DEC DE LD A,D OR E JR NZ,FILL RET ;***************************** __PUT_VRAM: PUSH HL ;save register LD HL,INTERRUPT_FLAGS ;point to interrupt flags BIT 0,(HL) ;is a VRAM I/O in progress? JR NZ,DO_PUT_VRAM_2 ;YES, so just do it ;(interrupts are already deferred) SET 0,(HL) ;NO, so mark it now in progress POP HL ;restore HL CALL DO_PUT_VRAM ;do the put JR VRAM_COMMON_EXIT ;__PUT_VRAM ;********** DO_PUT_VRAM_2: POP HL DO_PUT_VRAM: CALL SET_COUNT JP DO_WRITE_VRAM ;@@@ internal reference no deferral @@@ ; JP WRITE_VRAM ;***************************** __GET_VRAM: PUSH HL ;save register LD HL,INTERRUPT_FLAGS ;point to interrupt flags BIT 0,(HL) ;is a VRAM I/O in progress? JR NZ,DO_GET_VRAM_2 ;YES, so just do it ;(interrupts are already deferred) SET 0,(HL) ;NO, so mark it now in progress POP HL ;restore HL CALL DO_GET_VRAM ;do the get JR VRAM_COMMON_EXIT ;__GET_VRAM ;********** DO_GET_VRAM_2: POP HL DO_GET_VRAM: CALL SET_COUNT JP DO_READ_VRAM ;@@@ internal reference no deferral @@@ ; JP READ_VRAM ;***************************** __WR_SPR_ATTRIBUTE: PUSH HL ;save register LD HL,INTERRUPT_FLAGS ;point to interrupt flags BIT 0,(HL) ;is a VRAM I/O in progress? JR NZ,DO_WR_SPR_ATTRIBUTE_2 ;YES, so just do it ;(interrupts are already deferred) SET 0,(HL) ;NO, so mark it now in progress POP HL ;restore HL CALL DO_WR_SPR_ATTRIBUTE ;do the write JR VRAM_COMMON_EXIT ;__WR_SPR_ATTRIBUTE ;********** DO_WR_SPR_ATTRIBUTE_2: POP HL DO_WR_SPR_ATTRIBUTE: PUSH HL LD HL,(SPRITEATTRTBL) CALL SET_WRITE POP HL POP AF LD B,A LOOP_EVERY_SPRITE: LD A,(HL) ADD A,A ADD A,A INC HL PUSH HL LD L,A LD H,0 ADD HL,DE LD A,B LD B,4 LOOP_EVERY_BYTE: OUTI NOP NOP JR NZ,LOOP_EVERY_BYTE LD B,A POP HL DJNZ LOOP_EVERY_SPRITE RET SET_READ: XOR A DB 0C2H SET_WRITE: LD A,40H LD BC,(VDP_CTRL_PORT) OUT (C),L OR H OUT (C),A LD C,B RET ;********** SET_COUNT: PUSH IY LD C,A CP 4 JR NZ,NOT_COLOR_TBL_ACCESS LD A,(VDP_MODE_WORD) AND 02H JR Z,ADD_TO_BASE LD A,C NOT_COLOR_TBL_ACCESS: CP 2 JR Z,ADD_TO_BASE EX DE,HL ADD HL,HL ADD HL,HL OR A JR Z,HAVE_CNT ADD HL,HL HAVE_CNT: EX DE,HL EX (SP),HL ADD HL,HL ADD HL,HL JR Z,HAVE_CNT2 ADD HL,HL HAVE_CNT2: EX (SP),HL ADD_TO_BASE: LD A,C LD BC,VRAM_ADDR_TABLE PUSH HL LD H,00H LD L,A ADD HL,HL ADD HL,BC LD A,(HL) INC HL LD H,(HL) LD L,A ADD HL,DE EX DE,HL POP HL POP BC RET ;***************************** __CALC_OFFSET: PUSH HL BIT 7,D JR Z,ELSE_11 LD H,0FFH JR END_IF_11 ELSE_11: LD H,0 END_IF_11: LD L,D ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL BIT 7,E JR Z,ELSE_12 LD D,0FFH JR END_IF_12 ELSE_12: LD D,0 END_IF_12: ADD HL,DE EX DE,HL POP HL RET ;***************************** __PX_TO_PTRN_POS: PUSH HL PUSH BC LD B,3 PX_2_P_P_1: SRA D RR E DJNZ PX_2_P_P_1 POP BC LD HL,0FF80H BIT 7,D JR NZ,NEGTV ADD HL,DE POP HL RET NC LD E,7FH RET NEGTV: LD H,00H ADD HL,DE POP HL RET C LD E,80H RET ;***************************** DEL EQU 7FH __LOAD_ASCII LD DE,(PATTRNGENTBL) LD HL,00H LD BC,DEL-00H+1 ;***************************** ;Note: this routine is *CANNOT* be protected against interrupts which might ;occur while the SmartWriter ROM is switched in. Interrupts *MUST* be disabled. __PUT_ASCII: ADD HL,HL ADD HL,HL ADD HL,HL PUSH BC EX (SP),HL ADD HL,HL ADD HL,HL ADD HL,HL EX (SP),HL POP BC LD IX,0000H ADD IX,SP LD SP,TEMP_STACK PUSH IX LD A,(CUR_BANK) PUSH AF LD A,(MEM_CNFG00) PUSH BC CALL SWITCH_MEM POP BC PUSH DE LD DE,(LOC_IN_ALPHA) ADD HL,DE POP DE CALL DO_WRITE_VRAM ;@@@ internal reference, no deferral @@@ POP AF CALL SWITCH_MEM POP HL LD SP,HL RET ;***************************** __SWITCH_MEM: LD B,A LD A,(MEM_SWITCH_PORT) LD C,A OUT (C),B LD A,B LD (CUR_BANK),A RET ;***************************** ;Note: this routine is *CANNOT* be protected against interrupts which might ;occur while the OS7 ROM is switched in. Interrupts *MUST* be disabled. __PORT_COLLECTION: LD A,(CUR_BANK) PUSH AF LD A,(MEM_CNFG03) CALL SWITCH_MEM LD HL,VDP_CTRL_PORT LD A,(01D43H) LD (HL),A INC HL LD A,(01D47H) LD (HL),A INC HL LD A,(0114BH) LD (HL),A INC HL LD A,(01151H) LD (HL),A INC HL LD A,(01157H) LD (HL),A INC HL LD A,(01168H) LD (HL),A INC HL LD A,(0018EH) LD (HL),A POP AF CALL SWITCH_MEM RET ;***************************** __INIT_TABLE: PUSH DE EX DE,HL LD HL,VRAM_ADDR_TABLE ADD A,A LD C,A LD B,0 ADD HL,BC LD (HL),E INC HL LD (HL),D LD A,(VDP_MODE_WORD) AND 02H JR Z,INIT_TABLE80 LD A,C CP 2*3 JR C,INIT_TABLE80 LD A,D LD B,3 JR Z,CASE_OF_GEN OR E LD C,7FH JR Z,INIT_TABLE90 LD C,0FFH JR INIT_TABLE90 CASE_OF_GEN: LD C,B INC B OR E JR Z,INIT_TABLE90 LD C,7 JR INIT_TABLE90 INIT_TABLE80: LD HL,BASE_FACTORS ADD HL,BC LD B,(HL) LD A,E DIVIDE: SRL D RRA DJNZ DIVIDE LD C,A INC HL LD B,(HL) INIT_TABLE90: CALL WRITE_REGISTER POP DE RET ;********** BASE_FACTORS: DB 7 DB 5 DB 11 DB 6 DB 10 DB 2 DB 11 DB 4 DB 6 DB 3