Subject: Re: Apple 2 firmware reference Question. From: dempson@actrix.gen.nz (David Empson) Date: Wed, Nov 11, 1998 9Ç34 Message-id: <1did3w0.1iqjlhc1jhcvxwN@dempson.actrix.gen.nz> [Please don't post HTML-formatted messages to the Apple II newsgroups. Note also that followups are going to comp.sys.apple2.programmer.] -= Paul =- wrote: > > A year ago I bought a book at one computer warehouse called > "AppleIIgs Firmware Reference". [...] > So my question is HOW TO USE IT ?? I'll take it as read that you are a proficient assembly language programmer. The majority of the material in the firmware reference manual is of limited use to someone writing purely in BASIC. Almost all of it is only useful for 8-bit software. Native GS/OS-based software should be using the toolbox or GS/OS calls, not using the firmware. > For example "Generic Smart Port calls" ... Well we all know what port 5 > is so i found some call that dont make sense to me.. > > Init > > Standard call Extended Call > CMDNUM $05 $45 > CMDLIST Parameter count Parameter count > Unit numbers Unit number You don't want to use this particular call anyway. It reinitializes the SmartPort firwmware, rechecking for attached devices. The only time this call should ever be used is when the machine is booted, and the system already does it for you. (It can be used on SCSI cards after repartitioning a drive, so the firmware updates its cached partition tables, but this has to be done very carefully.) Let's look at a more useful example: a Status call, which gives you information about a device. (See pages 121-125.) The first point to note is that the SmartPort command set is implemented by lots of different I/O cards and firmware. Apart from the IIgs slot 5 firmware, I'm aware of the following implementations: "Liron" card in the IIe, IIc slot 5 firmware, standard slot "slinky" RAM cards, Apple SCSI cards, SuperDrive card. (There are bound to be others.) Each of these implementations differs slightly in capabilities. The base documentation for all SmartPort implementations is the IIgs firmware reference, plus additional information in the ROM 3 update to the IIgs firmware reference, and a series of technical notes. The parts that are common to every implementation are the way of identifying a SmartPort device, and how to call the driver. See page 114 for information on how to identify whether there is a SmartPort device in a particular slot. You need to check four identification bytes. You then need to look at the SmartPort ID byte if you want to know what general class of SmartPort driver you are dealing with, and in particular whether it supports extended calls. The "standard" calls are the original command set. They are limited to accessing the 64K address space of 8-bit Apple II models, and cannot handle devices with more than 2^24 blocks (8 gigabytes). The "extended" calls were added with the IIgs. They can access the full 16MB address space of the IIgs, and can support devices with up to 2^32 blocks (2 terabytes). To use the extended calls, you must be running on a IIgs. As far as I know, they are only implemented by the IIgs firmware and some recent Apple cards (High-Speed SCSI and SuperDrive). It is also important to check whether the specific device supports extended calls before making use of them. Unless you need to access data outside bank 0, there is no practical reason to use the extended calls, so I'll stick to standard calls for the rest of this discussion. After locating a SmartPort device, you need to get the entry point for the driver. This is described on page 115. The correct way to implement this is to set up a JSR instruction somewhere in your code and patch the address of the driver into the instruction. The low byte of the driver address is calculated by looking up the contents of $CnFF and adding 3. The high byte of the driver address is $Cn. (n is the slot number.) As described on page 120, SmartPort calls work in a similar way to a ProDOS MLI call. The JSR call to the driver is followed by a byte which contains the command number, and the address of the command list (two bytes for a standard call, four bytes for an extended call). After the driver returns, code will continue to execute after these fields (at the BCS ERROR instruction in both examples shown). So, given that you have located a SmartPort device and patched in the entry point, we want to do a Status call. The command number is zero, so the byte after the patched JSR must be set to zero. The command list pointer should be set to point to another memory area where you will set up the command list. The command list starts with a parameter count (always a byte). In the case of the Status call, this is followed by a Unit number (byte), Status List pointer (word) and status code (byte). You must set up all these fields before issuing the call. The parameter count must be set to 3. The unit number must be set to the logical device number you want to get status from (the first device is 1, or you can sometimes use 0 to refer to the SmartPort itself). The status list pointer points to yet another buffer area, which varies in size depending on the call. The status code is one of the values given on page 122. Let's say we want the device information block, so that would be status code 3. For this call, SmartPort will be returning 25 bytes of data (see page 123), so there must be at least 25 bytes that can be safely overwritten, starting at the location pointed to by the status list pointer. All done! We can now call the SmartPort driver, via our patched JSR instruction. On return, the carry flag is set if there is an error (with A containing the error code). If there was no error, then the status list will have been filled in as shown on pages 122-124. This is most interesting (and safest) if you put a write-protected 3.5" disk into the drive. You will get a status byte indicating a write protected disk, with other information as appropriate. Note that the infomration on page 124 which describes the device type and subtype bytes is misleading. There is a more accurate description in one of the SmartPort technical notes. Here are some code snippets that do the necessary parts (Merlin assembler syntax, apart from the comment lines probably being too long). I'll limit it to 6502 instructions, so it will work on any Apple II, and I'm not trying to break any speed or efficiency records. This is completely untested, so make sure you don't have any important data accessible when running it. I suggest turning off any hard drives as a safety precaution. SLOTPTR EQU $00 ; Pointer to my slot (word) FIND_SMARTPORT * This function scans the slots to locate a SmartPort. * On entry, X=$Cx, where x is the first slot to be checked. * On exit, X=$Cy, where y is the highest numbered slot less than or * equal to x which contains SmartPort firmware. If no SmartPort * is found, C=1 and A=$00. LDA #$00 STA SLOTPTR ; Set up the pointer TRYSLOT STX SLOTPTR+1 LDY #$01 LDA (SLOTPTR),Y ; Check the first ID byte CMP #$20 BNE NOT_HERE LDY #$03 LDA (SLOTPTR),Y ; and the second one CMP #$00 BNE NOT_HERE LDY #$05 LDA (SLOTPTR),Y ; and the third one CMP #$03 BNE NOT_HERE LDY #$07 LDA (SLOTPTR),Y ; and the fourth one CMP #$00 BNE NOT_HERE LDX SLOTPTR+1 ; Match! Get the address back CLC RTS NOT_HERE LDX SLOTPTR+1 ; Mismatch DEX ; Go down one slot CPX #$C1 BCS TRY_SLOT ; Stop once we have gone past slot 1 LDX #$00 SEC ; Error - no SmartPort found RTS SETUP_SMARTPORT * This function sets up the SP_CALL function for calling the * SmartPort driver. On entry, X=$Cx, where x is the slot number * containing a SmartPort driver. This should be checked via * FIND_SMARTPORT if necessary - don't assume there is a SmartPort * device in slot 5, for example! LDA #$00 STA SLOTPTR ; Set up the pointer STX SLOTPTR+1 LDY #$FF LDA (SLOTPTR),Y ; Get the ProDOS driver entry point CLC ADC #$03 ; Get the SmartPort driver entry point STA SP_CALL+1 ; Store in the JSR STX SP_CALL+2 ; also store the high byte RTS SP_GETDIB * This function gets the device information block from a specified * device on the active SmartPort. The SETUP_SMARTPORT routine must * have been called first. * On entry, A contains the device number (1, 2, 3, ...). * On exit, the STATLIST variable is filled in with the device * information block, unless an error occurred. C=1 if there was * an error, and A contains the error code. STA ST_UNIT ; Store the unit number LDA #$00 STA CMDNUM ; Set up the command number LDA #STATUS_PB STA CMDLISTP+1 ; Set up the command list pointer LDA #STATLIST STA ST_LISTP+1 ; Set up the status list pointer LDA #$03 STA ST_CODE ; Set up the status code (GetDIB) JMP SP_CALL ; Go to the generic SmartPort call handler * Insert additional functions here to implement other SmartPort calls * as desired SP_CALL * This function is patched to call an arbitrary SmartPort driver, * using an arbitrary command number and command list. JSR $FF58 ; Operand is patched with driver address CMDNUM DS 1 ; Patched with command number CMDLISTP DS 2 ; Patched with command list pointer RTS ; Return to caller STATUS_PB * Parameter block for issuing status calls DFB $03 ; Parameter count ST_UNIT DS 1 ; Unit number goes here ST_LISTP DS 2 ; Status list pointer goes here ST_CODE DS 1 ; Status code goes here STATLIST DS 25 ; General purpose status list buffer With a little wrapper code, this could be extended into a program that searches for every SmartPort in the system, determines the number of devices supported by each SmartPort (using a Status call to unit 0 with status code 0 - see page 125), gets information about each device, and prints the whole lot out on the screen. -- David Empson dempson@actrix.gen.nz Snail mail: P.O. Box 27-103, Wellington, New Zealand