*** ScR *** incdir include: AUTO wo devs:ber_serial.device\ ; Klude for bug in Asm-One (does not handle MEXIT properly!) EXEC_INITIALIZERS_I SET 1 INITBYTE MACRO ; &offset,&value IFLE (\1)-255 ;If offset <=255 DC.B $a0,\1 ;use byte offset DC.B \2,0 ELSE DC.B $e0,0 DC.W \1 DC.B \2,0 ENDC ENDM INITWORD MACRO ; &offset,&value IFLE (\1)-255 ;If offset <=255 DC.B $90,\1 ;use byte offset DC.W \2 ELSE DC.B $d0,0 DC.W \1 DC.W \2 ENDC ENDM INITLONG MACRO ; &offset,&value IFLE (\1)-255 ;If offset <=255 DC.B $80,\1 ;use byte offset DC.L \2 ELSE DC.B $c0,0 DC.W \1 DC.L \2 ENDC ENDM ; Let's redefine the PRINTF macro too... IFND PRINTF PRINTF MACRO ; level,<string>,... IFGE DEBUG_DETAIL-\1 .PUSHCOUNT SET 0 IFNC '\9'," move.l \9,-(sp) .PUSHCOUNT SET .PUSHCOUNT+4 ENDC IFNC '\8'," move.l \8,-(sp) .PUSHCOUNT SET .PUSHCOUNT+4 ENDC IFNC '\7'," move.l \7,-(sp) .PUSHCOUNT SET .PUSHCOUNT+4 ENDC IFNC '\6'," move.l \6,-(sp) .PUSHCOUNT SET .PUSHCOUNT+4 ENDC IFNC '\5'," move.l \5,-(sp) .PUSHCOUNT SET .PUSHCOUNT+4 ENDC IFNC '\4'," move.l \4,-(sp) .PUSHCOUNT SET .PUSHCOUNT+4 ENDC IFNC '\3'," move.l \3,-(sp) .PUSHCOUNT SET .PUSHCOUNT+4 ENDC movem.l a0/a1,-(sp) lea.l .PSS(pc),A0 lea.l 4*2(SP),A1 BSR kprint_macro movem.l (sp)+,a0/a1 bra.s .PSE .PSS dc.b \2 IFEQ (\1&1) ;If even, add CR/LF par... dc.b 13,10 ENDC dc.b 0 even .PSE lea.l .PUSHCOUNT(sp),sp ENDC ;IFGE DEBUG_DETAIL-\1 ENDM ;PRINTF MACRO ENDC ;IFND PRINTF ; And now the rest of the includes... include devices/serial.i include dos/dos.i include exec/macros.i include exec/types.i include exec/devices.i ; include exec/initializers.i ; See above include exec/lists.i include exec/memory.i include exec/resident.i include exec/io.i include exec/ables.i include exec/errors.i include exec/tasks.i include hardware/custom.i include hardware/cia.i include hardware/intbits.i include libraries/expansion.i include libraries/configvars.i include libraries/configregs.i include lvo/exec_lib.i include lvo/dos_lib.i include lvo/expansion_lib.i ; include macros.i ; include Projekt:ber_serial/ber.i include myinc/macros.i include work:coding/asm-source/ber_serial/ber.i ;DEBUG_DETAIL SET 2 ; Number of serial ports supported UNITS EQU 32 ; Default settings READBUFFERSIZE EQU 512 EXTFLAGS EQU 0 BAUDRATE EQU 9600 BREAKTIME EQU 250000 TERMARRAY0 EQU $00000000 TERMARRAY1 EQU $00000000 WRITELEN EQU 8 READLEN EQU 8 STOPBITS EQU 1 SERFLAGS EQU 0 STATUS EQU 0 _custom EQU $dff000 ; Our device base STRUCTURE BERSERIAL,LIB_SIZE UBYTE berd_Flags UBYTE berd_Pad1 ; Now longword aligned APTR berd_SysLib APTR berd_ExpLib BPTR berd_SegList STRUCT berd_Units,UNITS*4 LABEL BERSERIAL_SIZEOF ; ..and the Unit structure STRUCTURE BERSERIALUBIT,UNIT_SIZE UBYTE berdu_UnitNum UBYTE berdu_Flags ; See below ; Now longword aligned UBYTE berdu_ErrorCode ; CMD_READ error code UBYTE berdu_lcrreg ; Copy of the LCR register UBYTE berdu_mcrreg ; Copy of the MCR register UBYTE berdu_Pad1 APTR berdu_Device STRUCT berdu_int,IS_SIZE UWORD berdu_Pad2 ; Now longword aligned STRUCT berdu_CmdList,MLH_SIZE STRUCT berdu_ReadList,MLH_SIZE STRUCT berdu_WriteList,MLH_SIZE APTR berdu_ConfigDev APTR berdu_Base ; Base address of this device's expansion board APTR berdu_ReadBuffer APTR berdu_ReadPointer APTR berdu_WritePointer ULONG berdu_CtrlChars ULONG berdu_ReadBufferSize ULONG berdu_ExtFlags ULONG berdu_BaudRate ULONG berdu_BreakTime STRUCT berdu_TermArray,TERMARRAY_SIZE UBYTE berdu_ReadLen UBYTE berdu_WriteLen UBYTE berdu_StopBits UBYTE berdu_SerFlags UWORD berdu_Status LABEL BERSERIALUNIT_SIZEOF ; berdu_Flags definitions ; Bit 1-3 used to fake interrupts BITDEF B,STOPPED,4 ; Set inside CMD_STOP/CMD_START BITDEF B,SENDING,5 ; Set when sending data ******************************************************************************* **** Entry ******************************************************************** ******************************************************************************* Entry: moveq #-1,d0 rts initDDescrip: ; STRUCTURE RT,0 DC.W RTC_MATCHWORD ; UWORD RT_MATCHWORD (Magic cookie) DC.L initDDescrip ; APTR RT_MATCHTAG (Back pointer) DC.L EndCode ; APTR RT_ENDSKIP (To end of this hunk) DC.B RTF_AUTOINIT ; UBYTE RT_FLAGS (magic-see "Init:") DC.B VERSION ; UBYTE RT_VERSION DC.B NT_DEVICE ; UBYTE RT_TYPE (must be correct) DC.B 0 ; BYTE RT_PRI DC.L berName ; APTR RT_NAME (exec name) DC.L idString ; APTR RT_IDSTRING (text string) DC.L Init ; APTR RT_INIT ; LABEL RT_SIZE VERSION EQU 33 REVISION EQU 1 berName: dc.b "ber_serial.device",0 dc.b "$VER: " idString: dc.b "ber_serial.device 33.2 (5.6.97)",13,10,0 ExLibName dc.b "expansion.library",0 even Init: DC.L BERSERIAL_SIZEOF ; data space size DC.L funcTable ; pointer to function initializers DC.L dataTable ; pointer to data initializers DC.L initRoutine ; routine to run funcTable: dc.l Open dc.l Close dc.l Expunge dc.l Null dc.l BeginIO dc.l AbortIO dc.l -1 dataTable: INITBYTE LN_TYPE,NT_DEVICE ;Must be LN_TYPE! INITLONG LN_NAME,berName INITBYTE LIB_FLAGS,LIBF_SUMUSED!LIBF_CHANGED INITWORD LIB_VERSION,VERSION INITWORD LIB_REVISION,REVISION INITLONG LIB_IDSTRING,idString DC.W 0 ;terminate list even ******************************************************************************* **** initRoutine ************************************************************** ******************************************************************************* ; This call is single-threaded by exec; please read the description for ; "Open" below. ; ;---------------------------------------------------------------------- initRoutine: pushm std move.l d0,a5 move.l a6,berd_SysLib(a5) ; faster access than move.l 4.w,a6 move.l a0,berd_SegList(a5) ; Open expansion.library lea ExLibName(pc),A1 moveq #0,D0 call OpenLibrary move.l d0,berd_ExpLib(a5) beq .error lea dosname(pc),a1 moveq #0,d0 call OpenLibrary move.l d0,dosbase move.l a5,d0 .exit PRINTF 2,"Device initialized: 0x%08lx",d0 popm std rts .error: moveq #0,d0 bra .exit dosname: dc.b "dos.library",0 even dosbase dc.l 0 ;---------------------------------------------------------------------- ; ; Here begins the system interface commands. When the user calls ; OpenDevice/CloseDevice/Remdevice, this eventually gets translated ; into a call to the following routines (Open/Close/Expunge). ; Exec has already put our device pointer in a6 for us. ; ; IMPORTANT: ; These calls are guaranteed to be single-threaded; only one task ; will execute your Open/Close/Expunge at a time. ; ; For Kickstart V33/34, the single-threading method involves "Forbid". ; There is a good chance this will change. Anything inside your ; Open/Close/Expunge that causes a direct or indirect Wait() will break ; the Forbid(). If the Forbid() is broken, some other task might ; manage to enter your Open/Close/Expunge code at the same time. ; Take care! ; ; Since exec has turned off task switching while in these routines ; (via Forbid/Permit), we should not take too long in them. ; ;---------------------------------------------------------------------- ******************************************************************************* **** Open ********************************************************************* ******************************************************************************* ; Open sets the IO_ERROR field on an error. If it was successfull, ; we should also set up the IO_UNIT and LN_TYPE fields. ; exec takes care of setting up IO_DEVICE. ;in: * d0 unitnum * d1 flags * a1 iob * a6 device ;out: ; d0 device error code Open: addq.w #1,LIB_OPENCNT(a6) ;Fake an opener for duration of call <|> pushm std move.l a6,a5 move.l berd_SysLib(a5),a6 move.l a1,a2 ; save the iob ;------ see if the unit number is in range cmp.l #UNITS,d0 bhs.w .range_error ; unit number out of range (BHS) ;------ see if the unit is already initialized move.l d0,d2 ; save unit number lsl.l #2,d0 lea berd_Units(a5,d0.l),a4 move.l (a4),d0 beq .unitFree btst.b #SERB_SHARED,IO_SERFLAGS(a2) beq .busy_error ; We need exclusive access move.l (a4),a3 btst #SERB_SHARED,berdu_SerFlags(a3) beq .busy_error ; Already opened with exclusive access bra .unitOK .unitFree ;------ try and conjure up a unit bsr InitUnit ; d2:unitnum a5:devpoint ;------ see if it initialized OK tst.l (a4) beq .open_error moveq.l #0,d0 ; Don't need to re-zero it lea IOReqInit(pc),A1 call InitStruct ; a2: iob move.l a2,a1 move.l (a4),a3 bsr SetTheParams ; Initialize the hardware .unitOK move.l (a4),a3 ; unit pointer in a3 move.l a3,IO_UNIT(a2) move.l a2,a1 bsr UpdateIOReq ; Fill in current parameters ;------ mark us as having another opener addq.w #1,LIB_OPENCNT(a5) addq.w #1,UNIT_OPENCNT(a3) ;Internal bookkeeping ;------ prevent delayed expunges bclr #LIBB_DELEXP,LIB_FLAGS(a5) moveq #0,d0 move.b d0,IO_ERROR(a2) move.b #NT_REPLYMSG,LN_TYPE(a2) ;IMPORTANT: Mark IORequest as "complete" .end subq.w #1,LIB_OPENCNT(a5) ;** End of expunge protection <|> ; PRINTF 2,"Device opened: 0x%08lx, Error=%ld",a2,d0 popm std rts .range_error .open_error moveq #IOERR_OPENFAIL,d0 bra .error .busy_error moveq #SerErr_DevBusy,d0 bra .error .error move.b d0,IO_ERROR(a2) move.l d0,IO_DEVICE(a2) ;IMPORTANT: trash IO_DEVICE on open failure bra .end ******************************************************************************* **** Close ******************************************************************** ******************************************************************************* ; There are two different things that might be returned from the Close ; routine. If the device wishes to be unloaded, then Close must return ; the segment list (as given to Init). Otherwise close MUST return NULL. ;in: * a1 iob * a6 device ;out: ; d0 seglist or NULL Close: pushm std move.l a6,a5 move.l a1,a2 move.l IO_UNIT(a2),a3 ;Use this IORequest again and die! moveq.l #-1,d0 move.l d0,IO_UNIT(a2) ;We're closed... move.l d0,IO_DEVICE(a2) ;customers not welcome at this IORequest!! ;------ see if the unit is still in use subq.w #1,UNIT_OPENCNT(a3) bne .close_device bsr ExpungeUnit .close_device moveq #0,d0 ;------ mark us as having one fewer openers subq.w #1,LIB_OPENCNT(a5) ;------ see if there is anyone left with us open bne .end ;------ see if we have a delayed expunge pending btst #LIBB_DELEXP,LIB_FLAGS(a5) beq .end ;------ do the expunge move.l a5,a6 bsr Expunge .end PRINTF 2,"Device closed: SegList=0x%08lx",d0 popm std rts ;MUST return either zero or the SegList!!! ******************************************************************************* **** Expunge ****************************************************************** ******************************************************************************* ; Expunge is called by the memory allocator when the system is low on ; memory. ; ; There are two different things that might be returned from the Expunge ; routine. If the device is no longer open then Expunge may return the ; segment list (as given to Init). Otherwise Expunge may set the ; delayed expunge flag and return NULL. ; ; One other important note: because Expunge is called from the memory ; allocator, it may NEVER Wait() or otherwise take long time to complete. ;in: * a6 device ;out * d0 seglist/NULL Expunge: pushm std move.l a6,a5 move.l berd_SysLib(a5),a6 ;------ see if anyone has us open tst.w LIB_OPENCNT(a5) beq .1 ;------ it is still open. Set the delayed expunge flag bset #LIBB_DELEXP,LIB_FLAGS(a5) moveq #0,d0 bra .end .1 ;------ go ahead and get rid of us. Store our seglist in d2 move.l berd_SegList(a5),d2 ;------ unlink from device list move.l a5,a1 call Remove ;Remove first (before FreeMem) move.l berd_ExpLib(a5),d0 beq .noexp move.l d0,a1 call CloseLibrary .noexp ;------ free our memory (must calculate from LIB_POSSIZE & LIB_NEGSIZE) move.l a5,a1 ;Devicebase moveq #0,d0 move.w LIB_NEGSIZE(a5),d0 suba.l d0,a1 ;Calculate base of functions add.w LIB_POSSIZE(a5),d0 ;Calculate size of functions + data area call FreeMem ;------ set up our return value move.l d2,d0 .end PRINTF 2,"Device expunged: SegList=0x%08lx",d0 popm std rts ******************************************************************************* **** Null ********************************************************************* ******************************************************************************* Null: moveq #0,d0 rts ;The "Null" function MUST return NULL. ******************************************************************************* **** InitUnit ***************************************************************** ******************************************************************************* ;in: * d2 unit # * a5 device InitUnit: pushm std move.l berd_SysLib(a5),a6 ;------ allocate unit memory move.l #BERSERIALUNIT_SIZEOF,d0 move.l #MEMF_PUBLIC!MEMF_CLEAR,d1 call AllocMem tst.l d0 move.l d0,a3 beq .error moveq.l #0,d0 ; Don't need to re-zero it move.l a3,a2 ; InitStruct is initializing the UNIT lea.l .unitinit(pc),A1 call InitStruct move.l d2,d0 bsr iu_findcard tst.l d0 beq .error move.b d2,berdu_UnitNum(a3) ;initialize unit number move.l a5,berdu_Device(a3) ;initialize device pointer ;------ initialize the unit's lists lea MP_MSGLIST(a3),a0 NEWLIST a0 lea berdu_CmdList(a3),a0 NEWLIST a0 lea berdu_ReadList(a3),a0 NEWLIST a0 lea berdu_WriteList(a3),a0 NEWLIST a0 move.l a3,berdu_int+IS_DATA(a3); Pass unit addr to interrupt server lea berdu_int(a3),a1 moveq #INTB_PORTS,d0 call AddIntServer ;------ mark us as ready to go move.l d2,d0 ; unit number lsl.l #2,d0 move.l a3,berd_Units(a5,d0.l) ; set unit table .end popm std rts .error bsr FreeUnit bra .end .unitinit INITBYTE MP_FLAGS,PA_IGNORE ; Unit starts with a message port INITBYTE LN_TYPE,NT_MSGPORT INITLONG LN_NAME,berName INITBYTE berdu_int+LN_PRI,0 INITLONG berdu_int+IS_CODE,InterruptCode INITLONG berdu_int+LN_NAME,berName DC.W 0 **** iu_findcard ************************************************************** ; Find the serial card. This routine will scan for the unit:th usable ; 16550 register map. Each card can have several 16550s on board, but they ; must be placed right after each other in the address space. ;in: * d0 Unit ;out: * d0 NULL on failure iu_findcard: pushm std move.l d0,d2 move.l berd_ExpLib(a5),a6 clr.l berdu_ConfigDev(a3) .cardloop move.l #BER_MANUID,d0 move.l #BER_PRODID,d1 move.l berdu_ConfigDev(a3),a0 call FindConfigDev move.l d0,berdu_ConfigDev(a3) beq .error move.l d0,a0 move.l cd_BoardAddr(a0),a1 beq .error ; move.l a1,a2 ;.uartloop ; subq.l #1,d2 ; bmi .gotuart ; ; clr.b ber_spr+ber_SIZEOF(a1) ; move.b #$AB,ber_spr(a2) ; Store in first register map ; cmp.b #$AB,ber_spr+ber_SIZEOF(a1) ; Read in current ; beq .cardloop ; add.w #ber_SIZEOF,a1 ; bra .uartloop ;.gotuart ; Small selftest ; move.b #BERMCRF_DIAG,ber_mcr(a1) ; tst.b ber_msr(a0) ; tst.b ber_msr(a0) ; ; move.b #BERMCRF_DIAG!BERMCRF_DTR,ber_mcr(a1) ; cmp.b #BERMSRF_DSR!BERMSRF_DSRCHG,ber_msr(a1) ; bne .selftesterror ; ; move.b #BERMCRF_DIAG!BERMCRF_RTS,ber_mcr(a1) ; cmp.b #BERMSRF_CTS!BERMSRF_CTSCHG!BERMSRF_DSRCHG,ber_msr(a1) ; bne .selftesterror ; ; move.b #BERMCRF_DIAG!BERMCRF_OP1,ber_mcr(a1) ; cmp.b #BERMSRF_RI!BERMSRF_CTSCHG,ber_msr(a1) ; WHY?? ; bne .selftesterror ; ; move.b #BERMCRF_DIAG!BERMCRF_OP2,ber_mcr(a1) ; cmp.b #BERMSRF_CD!BERMSRF_CDCHG!BERMSRF_RICHG,ber_msr(a1) ; bne .selftesterror ; ; move.b #BERMCRF_DIAG,ber_mcr(a1) ; cmp.b #BERMSRF_CDCHG,ber_msr(a1) ; bne .selftesterror ; ; move.b #0,ber_mcr(a1) move.l a1,d0 .exit move.l d0,berdu_Base(a3) popm std rts .error moveq #0,d0 bra .exit .selftesterror move.b #0,ber_mcr(a1) bra .error ******************************************************************************* **** FreeUnit ***************************************************************** ******************************************************************************* ;in: * a3 unit * a5 device FreeUnit: pushm std move.l a3,d0 beq .exit bsr FreeHW move.l berd_SysLib(a5),a6 move.l berdu_ReadBuffer(a3),d0 beq .noreadbuff move.l d0,a1 move.l berdu_ReadBufferSize(a3),d0 call FreeMem .noreadbuff move.l a3,a1 move.l #BERSERIALUNIT_SIZEOF,d0 call FreeMem .exit popm std rts ******************************************************************************* **** ExpungeUnit ************************************************************** ******************************************************************************* ;in: * a3 unit * a5 device ExpungeUnit: pushm std move.l berd_SysLib(a5),a6 lea berdu_int(a3),a1 moveq #INTB_PORTS,d0 call RemIntServer ;Now remove the interrupt server ;------ save the unit number moveq #0,d2 move.b berdu_UnitNum(a3),d2 ;------ free the unit structure. bsr FreeUnit ;------ clear out the unit vector in the device lsl.l #2,d2 clr.l berd_Units(a5,d2.l) popm std rts ***************************************************************************** ; cmdtable is used to look up the address of a routine that will ; implement the device command. cmdtable: dc.l Invalid ; 0 CMD_INVALID dc.l Reset ; 1 CMD_RESET dc.l Read ; 2 CMD_READ dc.l Write ; 3 CMD_WRITE dc.l Update ; 4 CMD_UPDATE dc.l Clear ; 5 CMD_CLEAR dc.l Stop ; 6 CMD_STOP dc.l Start ; 7 CMD_START dc.l Flush ; 8 CMD_FLUSH dc.l Query ; 9 SDCMD_QUERY dc.l Break ; 10 SDCMD_BREAK dc.l SetParams ; 11 SDCMD_SETPARAMS cmdtable_end: ; S ; E ; T I ; P U N ; ABQFS CPW RV ; RRULTSLDRREA ; AEEUATEAIESL ; MARSROATTAEI ; SKYHTPREEDTD ; --------========--------======== IMMEDIATES EQU %00000000000000000000101100110011 ; --------========--------======== ; FEDCBA9876543210FEDCBA9876543210 ******************************************************************************* **** BeginIO ****************************************************************** ******************************************************************************* ; BeginIO starts all incoming IO. The IO is always processed immediately, ; but only the immediate commands ReplyMsg() it at once. ; ; IMPORTANT: ; The exec WaitIO() function uses the IORequest node type (LN_TYPE) ; as a flag. If set to NT_MESSAGE, it assumes the request is ; still pending and will wait. If set to NT_REPLYMSG, it assumes the ; request is finished. It's the responsibility of the device driver ; to set the node type to NT_MESSAGE before returning to the user. ;in: * a1 iob * a6 device BeginIO: PRINTF 2,"BeginIO 0x%08lx",a1 pushm std move.l a6,a5 move.b #NT_MESSAGE,LN_TYPE(a1) ;So WaitIO() is guaranteed to work move.l IO_UNIT(a1),a3 move.w IO_COMMAND(a1),d0 cmp.w #SER_DEVFINISH,d0 ;legal command? bhi .nocmd ;no, reject it. ; Immediate command? move.w #IMMEDIATES,d1 btst d0,d1 ;Had there been less than 8 commands ;to check, we could have used the ;mighty "btst d0,#IMMEDIATES" instr! bne .perform bclr #IOB_QUICK,IO_FLAGS(a1) ;We did NOT complete this quickly .perform bsr PerformIO .end popm std rts .nocmd move.b #IOERR_NOCMD,IO_ERROR(a1) bra .end ******************************************************************************* **** PerformIO **************************************************************** ******************************************************************************* ; PerformIO actually dispatches an io request. It is always called ; from BeginIO (thus on the callers's schedule). ; ; It expects a3 to already have the unit pointer in it. ; a5 has the device pointer (as always). ; a1 has the io request. ; Bounds checking has already been done on the I/O Request. ;in: * a1 iob * a3 unit * a5 device PerformIO: moveq #0,d0 move.b d0,IO_ERROR(A1) ; No error so far move.w IO_COMMAND(a1),d0 lsl.w #2,d0 ; Multiply by 4 to get table offset lea.l cmdtable(pc),a0 move.l 0(a0,d0.w),a0 jmp (a0) ;a1:iob a3:unit a5:devprt ******************************************************************************* **** TermIO ******************************************************************* ******************************************************************************* ; TermIO sends the IO request back to the user. ;in: * a1 iob * a3 unit * a5 device TermIO: pushm std move.l berd_SysLib(a5),a6 PRINTF 2,"Terminating 0x%08lx",a1 ; Clear some status flags in case of read error... cmp.w #CMD_READ,IO_COMMAND(a1) bne .readend ;; Should we enclose it with Disable()/Enable()? move.b berdu_ErrorCode(a3),d0 beq .noreaderror clr.b berdu_ErrorCode(a3) move.b d0,IO_ERROR(a1) .noreaderror tst.b IO_ERROR(a1) beq .readend bclr.b #IOSTB_WROTEBREAK,berdu_Status+1(a3) bclr.b #IOSTB_READBREAK,berdu_Status+1(a3) .readend btst #IOB_QUICK,IO_FLAGS(a1) bne .end call ReplyMsg ;a1:message .end popm std rts ******************************************************************************* **** AbortIO ****************************************************************** ******************************************************************************* ;AbortIO() is a REQUEST to "hurry up" processing of an IORequest. ;If the IORequest was already complete, nothing happens (if an IORequest ;is quick or LN_TYPE=NT_REPLYMSG, the IORequest is complete). ;The message must be replied with ReplyMsg(), as normal. ;in: * a1 iob * a6 device ;out: * d0 device error code AbortIO: pushm std move.l a6,a5 move.l a1,a2 move.l berd_SysLib(a5),a6 call Disable btst #IOB_QUICK,IO_FLAGS(a2) bne .error ; Cannot abort quick IO requests cmp.b #NT_REPLYMSG,LN_TYPE(a2) beq .error ; Cannot abort completed IO requests move.w IO_COMMAND(a2),d0 move.w #IMMEDIATES,d1 ; Remove if non-quick command btst d0,d1 beq .remove bra .error .remove move.b #IOERR_ABORTED,IO_ERROR(a1) move.l a2,a1 call Remove move.l a2,a1 bsr TermIO ; Terminate it moveq #IOERR_ABORTED,d0 .exit PRINTF 2,"Aborted 0x%08lx: Error %ld",a2,d0 call Enable popm std rts .error moveq #IOERR_NOCMD,d0 ; Return "AbortIO() request failed" bra .exit ***************************************************************************** ; ; Here begins the functions that implement the device commands ; all functions are called with: ; a1 -- a pointer to the io request block ; a3 -- a pointer to the unit ; a5 -- a pointer to the device ; ;---------------------------------------------------------------------- ******************************************************************************* **** Invalid/unimplemented commands ****************************************** ******************************************************************************* Invalid: Update: PRINTF 2,"CMD_INVALID/CMD_UPDATE" move.b #IOERR_NOCMD,IO_ERROR(a1) bra TermIO ******************************************************************************* **** Reset ******************************************************************** ******************************************************************************* Reset: pushm std PRINTF 2,"CMD_RESET" move.l berd_SysLib(a5),a6 move.l a1,a2 bsr RemoveAll moveq.l #0,d0 ; Don't need to re-zero it ; InitStruct is initializing the UNIT lea IOReqInit(pc),A1 call InitStruct move.b #SERFLAGS,IO_SERFLAGS(a2) move.l a2,a1 bsr SetTheParams move.l a2,a1 bsr UpdateIOReq move.l a2,a1 bsr TermIO popm std rts IOReqInit: ; Default settings INITLONG IO_CTLCHAR,SER_DEFAULT_CTLCHAR INITLONG IO_RBUFLEN,READBUFFERSIZE INITLONG IO_EXTFLAGS,EXTFLAGS INITLONG IO_BAUD,BAUDRATE INITLONG IO_BRKTIME,BREAKTIME INITLONG IO_TERMARRAY+TERMARRAY_0,TERMARRAY0 INITLONG IO_TERMARRAY+TERMARRAY_1,TERMARRAY1 INITBYTE IO_READLEN,READLEN INITBYTE IO_WRITELEN,WRITELEN INITBYTE IO_STOPBITS,STOPBITS DC.W 0 ;terminate list **** UpdateIOReq ************************************************************** UpdateIOReq: PRINTF 2,"UpdateIOReq" move.l berdu_CtrlChars(a3),IO_CTLCHAR(a1) move.l berdu_ReadBufferSize(a3),IO_RBUFLEN(a1) move.l berdu_ExtFlags(a3),IO_EXTFLAGS(a1) move.l berdu_BaudRate(a3),IO_BAUD(a1) move.l berdu_BreakTime(a3),IO_BRKTIME(a1) move.l berdu_TermArray+TERMARRAY_0(a3),IO_TERMARRAY+TERMARRAY_0(a1) move.l berdu_TermArray+TERMARRAY_1(a3),IO_TERMARRAY+TERMARRAY_1(a1) move.b berdu_ReadLen(a3),IO_READLEN(a1) move.b berdu_WriteLen(a3),IO_WRITELEN(a1) move.b berdu_StopBits(a3),IO_STOPBITS(a1) move.b berdu_SerFlags(a3),IO_SERFLAGS(a1) bsr GetStatus move.w d0,IO_STATUS(a1) rts **** RemoveAll **************************************************************** ; Aborts all IO requests. Used by CMD_RESET and CMD_FLUSH. RemoveAll: PRINTF 2,"RemoveAll" pushm std move.l a5,a6 .readloop move.l berdu_ReadList+MLH_HEAD(a3),a1 tst.l LN_SUCC(a1) beq .readremoved jsr DEV_ABORTIO(a6) bra .readloop .readremoved .writeloop move.l berdu_WriteList+MLH_HEAD(a3),a1 tst.l LN_SUCC(a1) beq .writeremoved jsr DEV_ABORTIO(a6) bra .writeloop .writeremoved .cmdloop move.l berdu_CmdList+MLH_HEAD(a3),a1 tst.l LN_SUCC(a1) beq .cmdremoved jsr DEV_ABORTIO(a6) bra .cmdloop .cmdremoved popm std rts ******************************************************************************* **** Read ********************************************************************* ******************************************************************************* Read: pushm std move.l berd_SysLib(a5),a6 move.l a1,a2 clr.l IO_ACTUAL(a2) ;; Fill from readbuffer first move.l berdu_ReadPointer(a3),a0 .loop cmp.l berdu_WritePointer(a3),a0 beq .bufferempty PRINTF 2,"CMD_READ (%ld): läs: 0x%08lx skriv: 0x%08lx",IO_LENGTH(a2),berdu_ReadPointer(a3),berdu_WritePointer(a3) move.w #$ff0,$dff180 move.l IO_DATA(a2),a1 move.l IO_ACTUAL(a2),d1 ;; Should check for XON/XOFF here move.b (a0)+,(a1,d1.l) move.l berdu_ReadBuffer(a3),d0 add.l berdu_ReadBufferSize(a3),d0 cmp.l d0,a0 bne .a0ok move.l berdu_ReadBuffer(a3),a0 .a0ok move.l a0,berdu_ReadPointer(a3) addq.l #1,d1 move.l d1,IO_ACTUAL(a2) cmp.l IO_LENGTH(a2),d1 blo .loop move.l a2,a1 bsr TermIO bra .exit .bufferempty call Disable lea berdu_ReadList(a3),a0 move.l a2,a1 ; a1:iob/message call AddTail call Enable .exit popm std rts ******************************************************************************* **** Write ******************************************************************** ******************************************************************************* Write: PRINTF 2,"CMD_WRITE" pushm std move.l berd_SysLib(a5),a6 call Disable clr.l IO_ACTUAL(a1) lea berdu_WriteList(a3),a0 call AddTail ; a1:iob/message call Enable btst #BB_SENDING,berdu_Flags(a3) bne .exit ; We are not sending any data, so we have to trigger the interrupt ; code manually move.b berdu_Flags(a3),d0 and.b #~BERISRF_MASK,d0 or.b #BERISRF_TXRDY,d0 move.b d0,berdu_Flags(a3) move.w #INTF_SETCLR!INTF_PORTS,_custom+intreq .exit popm std rts ******************************************************************************* **** Clear ******************************************************************** ******************************************************************************* Clear: PRINTF 2,"CMD_CLEAR" ; Atomic, no need to disable the interrupt move.l berdu_WritePointer(a3),berdu_ReadPointer(a3) clr.l IO_ACTUAL(a1) bra TermIO ******************************************************************************* **** Stop ********************************************************************* ******************************************************************************* Stop: PRINTF 2,"CMD_STOP" pushm std move.l berd_SysLib(a5),a6 call Disable clr.l IO_ACTUAL(a1) lea berdu_CmdList(a3),a0 call AddTail ; a1:iob/message call Enable btst #BB_SENDING,berdu_Flags(a3) bne .exit ; We are not sending any data, so we have to trigger the interrupt ; code manually move.b berdu_Flags(a3),d0 and.b #~BERISRF_MASK,d0 or.b #BERISRF_TXRDY,d0 move.b d0,berdu_Flags(a3) move.w #INTF_SETCLR!INTF_PORTS,_custom+intreq .exit popm std rts ******************************************************************************* **** Start ******************************************************************** ******************************************************************************* Start: PRINTF 2,"CMD_START" pushm std move.l berd_SysLib(a5),a6 call Disable clr.l IO_ACTUAL(a1) lea berdu_CmdList(a3),a0 call AddTail ; a1:iob/message call Enable btst #BB_SENDING,berdu_Flags(a3) bne .exit ; We are not sending any data, so we have to trigger the interrupt ; code manually move.b berdu_Flags(a3),d0 and.b #~BERISRF_MASK,d0 or.b #BERISRF_TXRDY,d0 move.b d0,berdu_Flags(a3) move.w #INTF_SETCLR!INTF_PORTS,_custom+intreq .exit popm std rts ******************************************************************************* **** Flush ******************************************************************** ******************************************************************************* Flush: PRINTF 2,"CMD_FLUSH" pushm std move.l a1,a2 move.l berd_SysLib(a5),a6 call Disable moveq #0,d6 moveq #0,d7 move.l berdu_ReadList+MLH_HEAD(a3),a1 ; First node in list tst.l LN_SUCC(a1) beq .noreadreq move.l a1,d6 call Remove ; Remove it temporary... .noreadreq move.l berdu_WriteList+MLH_HEAD(a3),a1 ; First node in list tst.l LN_SUCC(a1) beq .nowritereq move.l a1,d7 call Remove ; Remove it temporary... .nowritereq bsr RemoveAll ; Cancel all IO requests lea berdu_ReadList(a3),a0 ; ..and add it back move.l d6,a1 beq .noreadreq2 call AddHead .noreadreq2 lea berdu_WriteList(a3),a0 ; ..and add it back move.l a7,a1 beq .nowritereq2 call AddHead .nowritereq2 call Enable clr.l IO_ACTUAL(a1) move.l a2,a1 bsr TermIO popm std rts ******************************************************************************* **** Query ******************************************************************** ******************************************************************************* Query: PRINTF 2,"SDCMD_QUERY" pushm std move.l berd_SysLib(a5),a6 call Disable move.l a1,a2 bsr GetStatus move.w d0,IO_STATUS(a2) bclr.b #IOSTB_WROTEBREAK,berdu_Status+1(a3) bclr.b #IOSTB_READBREAK,berdu_Status+1(a3) move.l berdu_WritePointer(a3),d0 sub.l berdu_ReadPointer(a3),d0 bpl .plus add.l berdu_ReadBufferSize(a3),d0 .plus move.l d0,IO_ACTUAL(a2) a call Enable PRINTF 2,"Tecken i bufferten: %ld",d0 clr.l IO_ACTUAL(a2) move.l a2,a1 bsr TermIO popm std rts ******************************************************************************* **** Break ******************************************************************** ******************************************************************************* Break: PRINTF 2,"SDCMD_BREAK" pushm std move.l berd_SysLib(a5),a6 call Disable clr.l IO_ACTUAL(a1) lea berdu_CmdList(a3),a0 btst #SERB_QUEUEDBRK,IO_SERFLAGS(a1) beq .no_queued lea berdu_WriteList(a3),a0 .no_queued call AddTail ; a1:iob/message call Enable btst #BB_SENDING,berdu_Flags(a3) bne .exit ; We are not sending any data, so we have to trigger the interrupt ; code manually move.b berdu_Flags(a3),d0 and.b #~BERISRF_MASK,d0 or.b #BERISRF_TXRDY,d0 move.b d0,berdu_Flags(a3) move.w #INTF_SETCLR!INTF_PORTS,_custom+intreq .exit popm std rts ******************************************************************************* **** SetParams **************************************************************** ******************************************************************************* SetParams: PRINTF 2,"SDCMD_SETPARAMS" pushm std move.l a1,a2 bsr SetTheParams move.l a2,a1 bsr TermIO popm std rts **** SetTheParams ************************************************************* ; Initalizes the hardware to the supplied IO request. Called by CMD_RESET ; and SDCMD_SETPARAMS. SetTheParams: PRINTF 2,"SetTheParams" stp: pushm std move.l a1,a2 move.l berd_SysLib(a5),a6 call Disable moveq #FALSE,d7 ; Call InitHW? ; The only parameter that may be changed when the device is in use... move.b IO_SERFLAGS(a2),d0 and.b #SERF_XDISABLED,d0 move.b berdu_SerFlags(a3),d1 eor.b d1,d0 beq .nonewxonoff moveq #1,d7 ; Flag update move.b IO_SERFLAGS(a2),d0 and.b #SERF_XDISABLED,d0 move.b berdu_SerFlags(a3),d1 and.b #~SERF_XDISABLED,d1 or.b d1,d0 move.b d0,berdu_SerFlags(a3) bra .testrest .nonewxonoff ; Fail if the unit is active move.b #SerErr_DevBusy,IO_ERROR(a2) ;; move.b #SerErr_UnitBusy,IO_ERROR(a2) ; Not defined??? TSTLST2 berdu_ReadList(a3),a0 bne .error TSTLST2 berdu_WriteList(a3),a0 bne .error ; Device is not in use now, check the rest of the parameters .testrest move.b IO_SERFLAGS(a2),d0 cmp.b berdu_SerFlags(a3),d0 beq .nonewserflags moveq #1,d7 ; Flag update btst #SERB_RAD_BOOGIE,d0 beq .noboogie bset #SERB_XDISABLED,d0 bclr #SERB_PARTY_ON,d0 move.b #8,IO_READLEN(a2) move.b #8,IO_WRITELEN(a2) .noboogie move.b d0,berdu_SerFlags(a3) .nonewserflags move.l IO_CTLCHAR(a2),berdu_CtrlChars(a3) move.l IO_RBUFLEN(a2),d0 cmp.l berdu_ReadBufferSize(a3),d0 beq .nonewreadbuffer move.l berdu_ReadBuffer(a3),d0 beq .noreadbuff move.l d0,a1 move.l berdu_ReadBufferSize(a3),d0 call FreeMem .noreadbuff move.b #SerErr_BufErr,IO_ERROR(a2) move.l IO_RBUFLEN(a2),d0 move.l #MEMF_PUBLIC!MEMF_CLEAR,d1 call AllocMem move.l d0,berdu_ReadBuffer(a3) move.l d0,berdu_ReadPointer(a3) move.l d0,berdu_WritePointer(a3) beq .error move.l IO_RBUFLEN(a2),berdu_ReadBufferSize(a3) .nonewreadbuffer move.l IO_EXTFLAGS(a2),d0 cmp.l berdu_ExtFlags(a3),d0 beq .nonewextflags moveq #1,d7 ; Flag update move.l d0,berdu_ExtFlags(a3) .nonewextflags move.l IO_BAUD(a2),d0 cmp.l berdu_BaudRate(a3),d0 beq .nonewbaudrate moveq #1,d7 ; Flag update move.l d0,berdu_BaudRate(a3) .nonewbaudrate move.l IO_BRKTIME(a2),berdu_BreakTime(a3) move.l IO_TERMARRAY+TERMARRAY_0(a2),berdu_TermArray+TERMARRAY_0(a3) move.l IO_TERMARRAY+TERMARRAY_1(a2),berdu_TermArray+TERMARRAY_1(a3) move.b IO_READLEN(a2),d0 cmp.b berdu_ReadLen(a3),d0 beq .nonewreadlen moveq #1,d7 ; Flag update move.b d0,berdu_ReadLen(a3) .nonewreadlen move.b IO_WRITELEN(a2),d0 cmp.b berdu_WriteLen(a3),d0 beq .nonewwritelen moveq #1,d7 ; Flag update move.b d0,berdu_WriteLen(a3) .nonewwritelen move.b IO_STOPBITS(a2),d0 cmp.b berdu_StopBits(a3),d0 beq .nonewstopbits moveq #1,d7 ; Flag update move.b d0,berdu_StopBits(a3) .nonewstopbits moveq #0,d0 tst d7 beq .dontupdatehw bsr InitHW PRINTF 2,"InitWH returned: %ld",d0 .dontupdatehw move.b d0,IO_ERROR(a2) clr.l IO_ACTUAL(a2) .error call Enable popm std rts ******************************************************************************* **** InterruptCode ************************************************************ ******************************************************************************* ;in: * d0 scratch * d1 scratch * a0 scratch * a1 unit (scratch) * a5 InterruptCode (scratch) * a6 scratch InterruptCode: pushm std move.l a1,a3 move.l berdu_Device(a3),a5 move.l berd_SysLib(a5),a6 move.l berdu_Base(a3),a0 move.b ber_isr(a0),d2 move.b berdu_Flags(a3),d0 move.l d0,d1 and.b #BERISRF_MASK,d0 beq .dontfake bclr #BERISRB_NOIRQ,d2 .dontfake or.b d0,d2 and.b #~BERISRF_MASK,d1 move.b d1,berdu_Flags(a3) and.b #BERISRF_MASK,d2 cmp.b #BERISRF_LSR,d2 bne .notlsr move.b ber_lsr(a0),d0 .notlsr cmp.b #BERISRF_RXRDY,d2 bne .notrx bsr ic_read .notrx cmp.b #BERISRF_TXRDY,d2 bne .nottx bsr ic_write .nottx cmp.b #BERISRF_MSR,d2 bne .notmsr tst.b ber_msr(a0) .notmsr ic_exit: moveq #0,d0 ; Call next server in chain popm std ; Must not change the Z flag! rts *** CMD_READ ic_read pushm std move.l berdu_Base(a3),a4 bsr GetStatus btst #IOSTB_XOFFREAD+8,d0 bne .noreader .loop btst #BERLSRB_RX,ber_lsr(a4) beq .dontread move.l berdu_ReadList+MLH_HEAD(a3),a2 tst.l LN_SUCC(a2) beq .noreader move.l IO_DATA(a2),a0 move.l IO_ACTUAL(a2),d1 ;; Should check for XON/XOFF here move.b ber_rhr(a4),(a0,d1.l) addq.l #1,d1 move.l d1,IO_ACTUAL(a2) cmp.l IO_LENGTH(a2),d1 blo .dontterm move.l a2,a1 call Remove move.l a2,a1 bsr TermIO .dontterm bra .loop .noreader ; Fill circular buffer.... move.b ber_rhr(a4),d1 move.l berdu_ReadPointer(a3),d0 sub.l berdu_WritePointer(a3),d0 bpl .1 add.l berdu_ReadBufferSize(a3),d0 .1 subq.l #1,d0 bne .savechar move.b #SerErr_BufOverflow,berdu_ErrorCode(a3) ; Signal error tst.b ber_rhr(a4) bra .loop .savechar move.l berdu_WritePointer(a3),a0 move.b ber_rhr(a4),(a0)+ move.l berdu_ReadBuffer(a3),d0 add.l berdu_ReadBufferSize(a3),d0 cmp.l a0,d0 bne .2 move.l berdu_ReadBuffer(a3),a0 .2 move.l a0,berdu_WritePointer(a3) bra .loop .dontread popm std rts *** CMD_WRITE ic_write: pushm std bclr #BB_SENDING,berdu_Flags(a3) move.l berdu_Base(a3),a4 bsr GetStatus btst #IOSTB_XOFFWRITE+8,d0 bne .dontwrite .loop btst #BERLSRB_THREMPTY,ber_lsr(a4) beq .dontwrite move.l berdu_WriteList+MLH_HEAD(a3),a2 tst.l LN_SUCC(a2) beq .nowriter move.l IO_DATA(a2),a0 move.l IO_ACTUAL(a2),d1 move.b (a0,d1.l),d0 move.b d0,ber_thr(a4) bset #BB_SENDING,berdu_Flags(a3) and.l #$ff,d0 PRINTF 2,"%lc",d0 addq.l #1,d1 move.l d1,IO_ACTUAL(a2) tst.b d0 bne .notnull cmp.l #-1,IO_LENGTH(a2) beq .terminate .notnull cmp.l IO_LENGTH(a2),d1 blo .dontterm .terminate ; PRINTF 2,"INT: Writer 0x%08lx (0x%08lx, %ld)",a2,IO_DATA(a2),IO_LENGTH(a2) move.l a2,a1 call Remove .w8 btst #BERLSRB_THREMPTY,ber_lsr(a4) beq .w8 move.l a2,a1 bsr TermIO .dontterm bra .loop .nowriter .dontwrite popm std rts ******************************************************************************* **** Hardware code ************************************************************ ******************************************************************************* ;in: * a3 unit * a5 device ;out: * d0 status bits (as in IO_STATUS) GetStatus: moveq #0,d0 rts PRINTF 2,"GetStatus" pushm std move.w berdu_Status(a3),d0 move.b #%00011111,d0 ; All flags inactive move.l berdu_Base(a3),a0 move.b ber_lsr(a0),d1 btst #BERLSRB_OVERRUN,d1 beq .nooverrun move.b #SerErr_LineErr,berdu_ErrorCode(a3) .nooverrun ;; Is this the correct way to detect status word overruns? btst #BERLSRB_ERROR,d1 beq .noerror move.b d1,d2 and.w #BERLSRF_PARITY!BERLSRF_FRAME!BERLSRF_BREAK,d2 bne .goterror bset #IOSTB_OVERRUN+8,d1 .goterror btst #BERLSRB_PARITY,d1 beq .noparityerr move.b #SerErr_ParityErr,berdu_ErrorCode(a3) .noparityerr btst #BERLSRB_FRAME,d1 beq .noframingerr move.b #SerErr_LineErr,berdu_ErrorCode(a3) .noframingerr btst #BERLSRB_BREAK,d1 beq .nobreakerr bset #IOSTB_READBREAK+8,d0 .nobreakerr .noerror move.b ber_msr(a0),d1 btst #BERMSRB_CTS,d1 beq .nocts bclr #CIAB_COMCTS,d0 ; Active low .nocts btst #BERMSRB_DSR,d1 beq .nodsr bclr #CIAB_COMDSR,d0 ; Active low .nodsr btst #BERMSRB_RI,d1 beq .nori bset #CIAB_PRTRSEL,d0 ; Active high .nori btst #BERMSRB_CD,d1 beq .nocd bclr #CIAB_COMCD,d0 ; Active low .nocd move.b berdu_mcrreg(a3),d1 btst #BERMCRB_DTR,d1 beq .nodtr bclr #CIAB_COMDTR,d0 ; Active low .nodtr popm std rts ;in: * a3 unit * a5 device InitHW: move.l berdu_Base(a3),a0 PRINTF 2,"InitHW 0x%08lx",a0 ; Enable all interrupts move.b #BERIERF_RXENA!BERIERF_TXENA!BERIERF_LSENA!BERIERF_MSENA,ber_ier(a0) moveq #SerErr_InvParam,d0 tst.l berdu_ExtFlags(a3) bne .exit move.b #BERLCRF_DLABENA,ber_lcr(a0) ;enable dlab move.l #1843200,d0 move.l berdu_BaudRate(a3),d1 beq .bauderror divu d1,d0 ; Max 64 kbps! addq.l #8,d0 ; Round (+= 0.5) move.w d0,d1 lsr.w #4,d0 move.b d0,ber_dll(a0) lsr.w #8,d0 move.b d0,ber_dlm(a0) ; Check if legal baud rate and.w #~15,d1 beq .bauderror move.l #1843200,d0 divu d1,d0 and.l #$ffff,d0 sub.l berdu_BaudRate(a3),d0 bpl .1 neg.l d0 .1 lsl.l #4,d0 ; Allow approx 6% rate error cmp.l berdu_BaudRate(a3),d0 blo .baudOK .bauderror moveq #SerErr_BaudMismatch,d0 bra .exit .baudOK move.b berdu_ReadLen(a3),d0 cmp.b berdu_WriteLen(a3),d0 beq .samebits moveq #SerErr_InvParam,d0 bra .exit .samebits moveq #BERLCRF_LEN5,d1 cmp.b #5,d0 beq .bitsok moveq #BERLCRF_LEN6,d1 cmp.b #6,d0 beq .bitsok moveq #BERLCRF_LEN7,d1 cmp.b #7,d0 beq .bitsok moveq #BERLCRF_LEN8,d1 cmp.b #8,d0 beq .bitsok moveq #SerErr_InvParam,d0 bra .exit .bitsok ; moveq #SerErr_InvParam,d0 ; cmp.b #1,berdu_StopBits(a3) ; beq .stopsok ; cmp.b #2,berdu_StopBits(a3) ; bne .exit ; or.b #BERLCRF_LONGSTOP,d1 ;.stopsok ; ; btst #SERB_PARTY_ON,berdu_SerFlags(a3) ; beq .parityok ; or.b #BERLCRF_PARITYENA,d1 ;.parityok ; btst #SERB_PARTY_ODD,berdu_SerFlags(a3) ; bne .parityodd ; or.b #BERLCRF_PARITYEVEN,d1 ;.parityodd move.b d1,ber_lcr(a0) move.b d1,berdu_lcrreg(a3) ; Store for BREAK signal usage later move.b #BERMCRF_DTR,d1 move.b d1,ber_mcr(a0) move.b d1,berdu_mcrreg(a3) ; Store for RTS signal usage later moveq #0,d0 .exit rts ;in: * a3 unit * a5 device FreeHW: move.l berdu_Base(a3),a0 PRINTF 2,"FreeHW 0x%08lx",a0 ;.w8a ; btst #BB_SENDING,berdu_Flags(a3) ; bne .w8a .w8b btst #BERLSRB_THREMPTY,ber_lsr(a0) beq .w8b ; Disable all interrupts move.b #0,ber_ier(a0) move.b #0,ber_lcr(a0) move.b #0,ber_mcr(a0) rts ******************************************************************************* **** Debug code *************************************************************** ******************************************************************************* _LVORawPutChar EQU -$204 kprint_macro: pushm d0-d1/a0-a1/a6 bsr KPrintF ; move.l dosbase,a6 ; moveq #10,d1 ; call Delay ; moveq #10,d1 ;.l2 ; moveq #-1,d0 ;.l ; move.w d0,$dff180 ; dbf d0,.l ; dbf d1,.l2 popm d0-d1/a0-a1/a6 rts KPrintF: pushm a2/a6 lea KPutChar(pc),a2 base exec call RawDoFmt popm a2/a6 rts KPutChar: push a6 base exec call RawPutChar pop a6 rts EndCode: END
Go to the first, previous, next, last section, table of contents.