From: lecates@bach.udel.edu (Roy LeCates)
Newsgroups: comp.sys.apple2
Subject: Re: GS id
Date: 2 Jan 93 02:15:34 GMT
Organization: University of Delaware

In article <JOHNP.93Jan1172507@silver.lcs.mit.edu> johnp@silver.lcs.mit.edu (John Palaima) writes:
>I am interested in telling WHAT machine a program is running on to activate app
>ropriate title screens text coloring etc on the GS and //e and i need the addre
>sses to poke.. I trie both $fbb3 and $fbc0 on my //e and my friends GS and THE\
>Y MATCH.. i was told there are a total of 4 bytes i need to check..where are the other 2...

That information is located in the Apple II Miscellaneous Tech Notes.
Just for convenience, here it is!

------------------------------------------------------------

Apple II
Technical Notes
_____________________________________________________________________________
                                                  Developer Technical Support


Apple II Miscellaneous
#2:    Apple II Family Identification Routines 2.2

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:

o   A machine byte, containing one of seven values:
    $00 = Unknown machine
    $01 = Apple ][
    $02 = Apple ][+
    $03 = Apple /// in emulation mode
    $04 = Apple IIe
    $05 = Apple IIc
    $06 = Apple IIe Card for the Macintosh LC

    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).
o   A ROMlevel byte, indicating the revision of the firmware in the machine.  
    For example, there are currently five revisions of the IIc, two of the 
    IIe (unenhanced and enhanced), and three versions of the IIgs ROM (there 
    will always be some owners who have not yet upgraded from ROM 00 to ROM 
    01).  These versions are identified starting at $01 for the earliest.  
    Therefore, the current IIc will return ROMlevel = $05, the current IIgs 
    will return ROMlevel = $03, etc.  The routine will also return correct 
    values for future versions of the IIgs, as a convention has been 
    established for future ROM versions of that machine.
o   A memory byte, containing the amount of memory in the machine.  This byte 
    only has four values--0 (undefined), 48, 64, and 128.  Extra memory in an 
    Apple IIgs, or extra memory in an Apple IIe or IIc Memory Expansion card, 
    is not included.  Programs must take special considerations to use that 
    memory (if available), beyond those considerations required to use the 
    normal 128K of today's IIe and IIc.
o   If running on an Apple IIgs, three word-length fields are also returned.  
    These are the contents of the registers as returned by the ID routine in 
    the IIgs ROM, and they indicate several things about the machine.  See 
    Apple II Miscellaneous Technical Note #7 for more details.

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:

o    If you are running under ProDOS, you can remove the section that 
     determines how much memory is in the machine (starting at exit, line 
     127), since the MACHID byte (at $BF98) in ProDOS already contains this 
     information for you.  This change would cut the routine down to less 
     than one page of memory.
o    If you know the ROM is switched in when you call the routine, you can 
     remove the sections which save and restore the language card state.  Be 
     careful in doing so, however, because the memory-determination routines 
     switch out the ROM to see if a language card exists.
o    If you need to know if a IIe is a 64K machine with a non-extended 
     80-column card, you may put your own identifying routines in after line 
     284.  NoAux is only reached if there is an 80-column card but only 64K 
     of memory.



How It Works

The identification routine does the following things:

o    Disables interrupts
o    Saves four bytes from the language card areas so they may be restored 
     later
o    Identifies all machines by a table look-up procedure
o    Calls 16-bit ID routine to distinguish IIgs from other machines of any 
     kind, and returns values in appropriate locations if IIgs ID routine 
     returns any useful information in the registers
o    Identifies memory configuration:
o    If Apple /// emulation, there is 48K
o    If Apple ][ or ][+, tests for presence of language card and returns 64K 
     if present, otherwise, returns 48K
o    If Apple IIc or IIgs, returns 128K
o    If Apple IIe, tries to identify auxiliary memory
o    If reading auxiliary memory, it must be there
o    If reading alternate zero page, auxiliary memory is present
o    If none of this is conclusive:
o    Exchanges a section of the zero page with a section of code that 
     switches memory banks.  The code executes in the zero page and does not 
     get switched out when we attempt to switch in the auxiliary RAM.
o    Jumps to relocated code on page zero:
o    Switches in auxiliary memory for reading and writing
o    Stores a value at $800 and sees if the same value appears at $C00.  If   
     so, no auxiliary memory is present (the non-extended 80-column card has 
     sparse memory mapping which causes $800 and $C00 to be the same 
     location).
o    Changes value at $C00 and sees if the value at $800 changes as well.  If 
     so, no auxiliary memory.  If not, then there is 128K available
o    Switches main memory back in for reading and writing
o    Puts the zero page back like we found it
o    Returns memory configuration found (either 64K or 128K)
o    Restores language card and ROM state from four saved bytes
o    Restores interrupt status
o    Returns to caller


           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


Further Reference
_____________________________________________________________________________
  o  Apple II Miscellaneous Technical Note #7, Apple II Family Identification

-- 
------------------------------------------------------------------
Roy M. LeCates       EE Undergraduate        lecates@bach.udel.edu
University of Delaware Electrical Engineering and Computer Science
------------------------------------------------------------------
Newsgroups: comp.sys.apple2