Apple II Technical Notes Developer Technical Support
Revised by: Jim Luther May 1991
Revised by: Matt Deatherage & Keith Rollin November 1988
Revised by: Pete McDonald January 1986
This Technical Note presents a new version of the Apple II Family Identification Routine, a sample piece of code which shows how to identify various Apple II computers and their memory configurations.
Changes since November 1988: Converted the identification routine from Apple II Assembler/Editor (EDASM) source code to Apple IIgs Programmer's Workshop (APW) Assembler source code. Added the Apple IIe Card for the Macintosh LC to the identification routine's lookup table and memory check routine. Made minor corrections to text.
Why Identification Routines?
Although we present the Apple II family identification bytes in Apple II Miscellaneous Technical Note #7, many people would prefer a routine they can simply plug into their own program and call. In addition, this routine serves as a small piece of sample code, and there is no reason for you to reinvent the wheel.
Most of the interesting part of the routine consists of identifying the memory configuration of the machine. On an Apple IIe, the routine moves code into the zero page to test for the presence of auxiliary memory. (A IIe with a non-extended 80-column card is a configuration still found in many schools throughout the country.)
The actual identification is done by a table-lookup method.
What the Routine Returns
This version (2.2) of the identification routine returns several things:
In addition, if the high bit of the byte is set, the machine is a IIgs or equivalent. For all current Apple IIgs computers, the value returned in machine is $84 (high bit set to signify Apple IIgs and $04 because it matches the ID bytes of an enhanced Apple IIe).
In addition to these features, most of the addressing done in the routine is by label. If you wish things to be stored in different places, simply changing the labels will often do it.
Limitations and Improvements
As sample code, you might have already guessed that this is not the most compact, efficient way of identifying these machines. Some improvements you might incorporate if using these routines include:
How It Works
The identification routine does the following things:
keep ID2.2 list on org $2000 longa off longi off
********************************************* * * * Apple II Family Identification Program * * * * Version 2.2 * * * * March, 1990 * * * * Includes support for the Apple IIe Card * * for the Macintosh LC. * * * *********************************************
; First, some global equates for the routine:
PROGRAM start
IIplain equ $01 ;Apple II IIplus equ $02 ;Apple II+ IIIem equ $03 ;Apple /// in emulation mode IIe equ $04 ;Apple IIe IIc equ $05 ;Apple IIc IIeCard equ $06 ;Apple IIe Card for the Macintosh LC
safe equ $0001 ;start of code relocated to zp location equ $06 ;zero page location to use
test1 equ $AA ;test byte #1 test2 equ $55 ;lsr of test1 test3 equ $88 ;test byte #3 test4 equ $EE ;test byte #4
begpage1 equ $400 ;beginning of text page 1 begpage2 equ $800 ;beginning of text page 2 begsprse equ $C00 ;byte after text page 2
clr80col equ $C000 ;disable 80-column store set80col equ $C001 ;enable 80-column store rdmainram equ $C002 ;read main ram rdcardram equ $C003 ;read aux ram wrmainram equ $C004 ;write main ram wrcardram equ $C005 ;write aux ram rdramrd equ $C013 ;are we reading aux ram? rdaltzp equ $C016 ;are we reading aux zero page? rd80col equ $C018 ;are we using 80-columns? rdtext equ $C01A ;read if text is displayed rdpage2 equ $C01C ;read if page 2 is displayed txtclr equ $C050 ;switch in graphics txtset equ $C051 ;switch in text txtpage1 equ $C054 ;switch in page 1 txtpage2 equ $C055 ;switch in page 2 ramin equ $C080 ;read LC bank 2, write protected romin equ $C081 ;read ROM, 2 reads write enable LC lcbank1 equ $C08B ;LC bank 1 enable
lc1 equ $E000 ;bytes to save for LC lc2 equ $D000 ;save/restore routine lc3 equ $D400 lc4 equ $D800
idroutine equ $FE1F ;IIgs id routine
; Start by saving the state of the language card banks and ; by switching in main ROM.
strt php ;save the processor state sei ;before disabling interrupts lda lc1 ;save four bytes from sta save ;ROM/RAM area for later lda lc2 ;restoring of RAM/ROM sta save+1 ;to original condition lda lc3 sta save+2 lda lc4 sta save+3 lda $C081 ;read ROM lda $C081 lda #0 ;start by assuming unknown machine sta machine sta romlevel
IdStart lda location ;save zero page locations sta save+4 ;for later restoration lda location+1 sta save+5 lda #$FB ;all ID bytes are in page $FB sta location+1 ;save in zero page as high byte ldx #0 ;init pointer to start of ID table loop lda IDTable,x ;get the machine we are testing for sta machine ;and save it lda IDTable+1,x ;get the ROM level we are testing for sta romlevel ;and save it ora machine ;are both zero? beq matched ;yes - at end of list - leave
loop2 inx ;bump index to loc/byte pair to test inx lda IDTable,x ;get the byte that should be in ROM beq matched ;if zero, we're at end of list sta location ;save in zero page ldy #0 ;init index for indirect addressing lda IDTable+1,x ;get the byte that should be in ROM cmp (Location),y ;is it there? beq loop2 ;yes, so keep on looping
loop3 inx ;we didn't match. Scoot to the end of the inx ;line in the ID table so we can start lda IDTable,x ;checking for another machine bne loop3 inx ;point to start of next line bne loop ;should always be taken
matched anop
; Here we check the 16-bit ID routine at idroutine ($FE1F). If it ; returns with carry clear, we call it again in 16-bit ; mode to provide more information on the machine.
idIIgs sec ;set the carry bit jsr idroutine ;Apple IIgs ID Routine bcc idIIgs2 ;it's a IIgs or equivalent jmp IIgsOut ;nope, go check memory idIIgs2 lda machine ;get the value for machine ora #$80 ;and set the high bit sta machine ;put it back clc ;get ready to switch into native mode xce php ;save the processor status rep #$30 ;sets 16-bit registers longa on longi on jsr idroutine ;call the ID routine again sta IIgsA ;16-bit store! stx IIgsX ;16-bit store! sty IIgsY ;16-bit store! plp ;restores 8-bit registers xce ;switches back to whatever it was before longa off longi off ldy IIgsY ;get the ROM vers number (starts at 0) cpy #$02 ;is it ROM 01 or 00? bcs idIIgs3 ;if not, don't increment iny ;bump it up for romlevel idIIgs3 sty romlevel ;and put it there cpy #$01 ;is it the first ROM? bne IIgsOut ;no, go on with things lda IIgsY+1 ;check the other byte too bne IIgsOut ;nope, it's a IIgs successor lda #$7F ;fix faulty ROM 00 on the IIgs sta IIgsA IIgsOut anop
****************************************** * This part of the code checks for the * * memory configuration of the machine. * * If it's a IIgs, we've already stored * * the total memory from above. If it's * * a IIc or a IIe Card, we know it's * * 128K; if it's a ][+, we know it's at * * least 48K and maybe 64K. We won't * * check for less than 48K, since that's * * a really rare circumstance. * ******************************************
exit lda machine ;get the machine kind bmi exit128 ;it's a 16-bit machine (has 128K) cmp #IIc ;is it a IIc? beq exit128 ;yup, it's got 128K cmp #IIeCard ;is it a IIe Card? beq exit128 ;yes, it's got 128K cmp #IIe ;is it a IIe? bne contexit ;yes, go muck with aux memory jmp muckaux contexit cmp #IIIem ;is it a /// in emulation? bne exitII ;nope, it's a ][ or ][+ lda #48 ;/// emulation has 48K jmp exita exit128 lda #128 ;128K exita sta memory exit1 lda lc1 ;time to restore the LC cmp save ;if all 4 bytes are the same bne exit2 ;then LC was never on so lda lc2 ;do nothing cmp save+1 bne exit2 lda lc3 cmp save+2 bne exit2 lda lc4 cmp save+3 beq exit6 exit2 lda $C088 ;no match! so turn first LC lda lc1 ;bank on and check cmp save beq exit3 lda $C080 jmp exit6 exit3 lda lc2 cmp save+1 ;if all locations check beq exit4 ;then do more more else lda $C080 ;turn on bank 2 jmp exit6 exit4 lda lc3 ;check second byte in bank 1 cmp save+2 beq exit5 lda $C080 ;select bank 2 jmp exit6 exit5 lda lc4 ;check third byte in bank 1 cmp save+3 beq exit6 lda $C080 ;select bank 2 exit6 plp ;restore interrupt status lda save+4 ;put zero page back sta location lda save+5 ;like we found it sta location+1 rts ;and go home.
exitII lda lcbank1 ;force in language card lda lcbank1 ;bank 1 ldx lc2 ;save the byte there lda #test1 ;use this as a test byte sta lc2 eor lc2 ;if the same, should return zero bne noLC lsr lc2 ;check twice just to be sure lda #test2 ;this is the shifted value eor lc2 ;here's the second check bne noLC stx lc2 ;put it back! lda #64 ;there's 64K here jmp exita noLC lda #48 ;no restore - no LC! jmp exita ;and get out of here
muckaux ldx rdtext ;remember graphics in X lda rdpage2 ;remember current video display asl A ;in the carry bit lda #test3 ;another test character bit rd80col ;remember video mode in N sta set80col ;enable 80-column store php ;save N and C flags sta txtpage2 ;set page two sta txtset ;set text ldy begpage1 ;save first character sta begpage1 ;and replace it with test character lda begpage1 ;get it back sty begpage1 ;and put back what was there plp bcs muck2 ;stay in page 2 sta txtpage1 ;restore page 1 muck1 bmi muck2 ;stay in 80-columns sta $c000 ;turn off 80-columns muck2 tay ;save returned character txa ;get graphics/text setting bmi muck3 sta txtclr ;turn graphics back on muck3 cpy #test3 ;finally compare it bne nocard ;no 80-column card! lda rdramrd ;is aux memory being read? bmi muck128 ;yup, there's 128K! lda rdaltzp ;is aux zero page used? bmi muck128 ;yup! ldy #done-start move ldx start-1,y ;swap section of zero page lda |safe-1,y ;code needing safe location during stx safe-1,y ;reading of aux mem sta start-1,Y dey bne move jmp |safe ;jump to safe ground back php ;save status ldy #done-start ;move zero page back move2 lda start-1,y sta |safe-1,y dey bne move2 pla bcs noaux isaux jmp muck128 ;there is 128K
* You can put your own routine at "noaux" if you wish to * distinguish between 64K without an 80-column card and * 64K with an 80-column card.
noaux anop
nocard lda #64 ;only 64K jmp exita muck128 jmp exit128 ;there's 128K
* This is the routine run in the safe area not affected * by bank-switching the main and aux RAM.
start lda #test4 ;yet another test byte sta wrcardram ;write to aux while on main zero page sta rdcardram ;read aux ram as well sta begpage2 ;check for sparse memory mapping lda begsprse ;if sparse, these will be the same cmp #test4 ;value since they're 1K apart bne auxmem ;yup, there's 128K! asl begsprse ;may have been lucky so we'll lda begpage2 ;change the value and see what happens cmp begsprse bne auxmem sec ;oops, no auxiliary memory bcs goback auxmem clc goback sta wrmainram ;write main RAM sta rdmainram ;read main RAM jmp back ;continue with program in main mem done nop ;end of relocated program marker
* The storage locations for the returned machine ID:
machine ds 1 ;the type of Apple II romlevel ds 1 ;which revision of the machine memory ds 1 ;how much memory (up to 128K) IIgsA ds 2 ;16-bit field IIgsX ds 2 ;16-bit field IIgsY ds 2 ;16-bit field save ds 6 ;six bytes for saved data
IDTable dc I1'1,1' ;Apple ][ dc H'B3 38 00' dc I1'2,1' ;Apple ][+ dc H'B3 EA 1E AD 00' dc I1'3,1' ;Apple /// (emulation) dc H'B3 EA 1E 8A 00' dc I1'4,1' ;Apple IIe (original) dc H'B3 06 C0 EA 00' ; Note: You must check for the Apple IIe Card BEFORE you ; check for the enhanced Apple IIe since the first ; two identification bytes are the same. dc I1'6,1' ;Apple IIe Card for the Macintosh LC (1st release) dc H'B3 06 C0 E0 DD 02 BE 00 00' dc I1'4,2' ;Apple IIe (enhanced) dc H'B3 06 C0 E0 00' dc I1'5,1' ;Apple IIc (original) dc H'B3 06 C0 00 BF FF 00' dc I1'5,2' ;Apple IIc (3.5 ROM) dc H'B3 06 C0 00 BF 00 00' dc I1'5,3' ;Apple IIc (Mem. Exp) dc H'B3 06 C0 00 BF 03 00' dc I1'5,4' ;Apple IIc (Rev. Mem. Exp.) dc H'B3 06 C0 00 BF 04 00' dc I1'5,5' ;Apple IIc Plus dc H'B3 06 C0 00 BF 05 00' dc I1'0,0' ;end of table end