Fixed some edge case language card issues and implemented false reads for absolute indexed addressing modes, improved test coverage

This commit is contained in:
Badvision 2024-08-05 00:36:49 -05:00
parent 1c26ecad3d
commit 519c561537
7 changed files with 997 additions and 23 deletions

View File

@ -414,6 +414,8 @@ public class MOS65C02 extends CPU {
ABSOLUTE_X(3, "$~2~1,X", (cpu) -> {
int address2 = cpu.getMemory().readWord(cpu.getProgramCounter() + 1, TYPE.READ_OPERAND, cpu.readAddressTriggersEvent, false);
int address = 0x0FFFF & (address2 + cpu.X);
// False read
cpu.getMemory().read(address, TYPE.READ_DATA, true, false);
cpu.setPageBoundaryPenalty((address & 0x00ff00) != (address2 & 0x00ff00));
cpu.setPageBoundaryApplied(true);
return address;
@ -421,6 +423,8 @@ public class MOS65C02 extends CPU {
ABSOLUTE_Y(3, "$~2~1,Y", (cpu) -> {
int address2 = cpu.getMemory().readWord(cpu.getProgramCounter() + 1, TYPE.READ_OPERAND, cpu.readAddressTriggersEvent, false);
int address = 0x0FFFF & (address2 + cpu.Y);
// False read
cpu.getMemory().read(address, TYPE.READ_DATA, true, false);
cpu.setPageBoundaryPenalty((address & 0x00ff00) != (address2 & 0x00ff00));
cpu.setPageBoundaryApplied(true);
return address;

View File

@ -16,6 +16,7 @@
package jace.apple2e.softswitch;
import jace.core.RAMEvent;
import jace.core.RAMEvent.TYPE;
/**
@ -23,32 +24,30 @@ import jace.core.RAMEvent.TYPE;
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public class Memory2SoftSwitch extends MemorySoftSwitch {
public Memory2SoftSwitch(String name, int offAddress, int onAddress, int queryAddress, TYPE changeType, Boolean initalState) {
super(name, offAddress, onAddress, queryAddress, changeType, initalState);
}
public Memory2SoftSwitch(String name, int[] offAddrs, int[] onAddrs, int[] queryAddrs, TYPE changeType, Boolean initalState) {
super(name, offAddrs, onAddrs, queryAddrs, changeType, initalState);
}
// The switch must be set true two times in a row before it will actually be set.
int count = 0;
int readCount = 0;
@Override
public void setState(boolean newState) {
public void setState(boolean newState, RAMEvent e) {
if (!newState) {
count = 0;
super.setState(newState);
super.setState(false);
readCount = 0;
} else {
count++;
if (count >= 2) {
super.setState(newState);
count = 0;
if (e.getType().isRead()) {
readCount++;
} else {
readCount = 0;
}
if (readCount >= 2) {
super.setState(true);
}
}
}
@Override
public String toString() {
return getName()+(getState()?":1":":0")+"~~"+count;
return getName()+(getState()?":1":":0")+"~~"+readCount;
}
}

View File

@ -114,8 +114,7 @@ public abstract class SoftSwitch {
@Override
protected void doEvent(RAMEvent e) {
if (!exclusionActivate.contains(e.getAddress())) {
// System.out.println("Access to "+Integer.toHexString(e.getAddress())+" ENABLES switch "+getName());
setState(!getState());
setState(!getState(), e);
}
}
};
@ -148,7 +147,7 @@ public abstract class SoftSwitch {
}
if (!exclusionActivate.contains(e.getAddress())) {
// System.out.println("Access to "+Integer.toHexString(e.getAddress())+" ENABLES switch "+getName());
setState(true);
setState(true, e);
}
}
};
@ -177,7 +176,7 @@ public abstract class SoftSwitch {
@Override
protected void doEvent(RAMEvent e) {
if (!exclusionDeactivate.contains(e.getAddress())) {
setState(false);
setState(false, e);
// System.out.println("Access to "+Integer.toHexString(e.getAddress())+" disables switch "+getName());
}
}
@ -250,6 +249,12 @@ public abstract class SoftSwitch {
});
}
// Most softswitches act the same regardless of the ram event triggering them
// But some softswitches are a little tricky (such as language card write) and need to assert extra conditions
public void setState(boolean newState, RAMEvent e) {
setState(newState);
}
public void setState(boolean newState) {
if (inhibit()) {
return;

View File

@ -246,10 +246,10 @@ public class TestProgram {
private void handleTrace(byte val) {
if (val == (byte)0x01) {
System.out.println("Trace on");
Full65C02Test.cpu.setTraceEnabled(true);
Emulator.withComputer(c->c.getCpu().setTraceEnabled(true));
} else {
System.out.println("Trace off");
Full65C02Test.cpu.setTraceEnabled(false);
Emulator.withComputer(c->c.getCpu().setTraceEnabled(false));
}
}
@ -274,6 +274,13 @@ public class TestProgram {
return this;
}
public TestProgram assertEquals(String message) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
String caller = stackTrace[2].toString();
_test(TestProgram.INDENT + TestProgram.Flag.IS_ZERO.code, message + "<<" + caller + ">>");
return this;
}
public TestProgram assertFlags(TestProgram.Flag... flags) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
String caller = stackTrace[2].toString();
@ -372,7 +379,11 @@ public class TestProgram {
+throwError %s
+ ; >> Test """.formatted(condition, code, errorNum));
}
public TestProgram throwError(String error) {
_test("", error);
return this;
}
/**
* Note the current breakpoint, helpful in understanding error locations
*
@ -447,6 +458,12 @@ public class TestProgram {
for (int i=0; i < maxTicks; i++) {
cpu.doTick();
tickCount++;
if (cpu.interruptSignalled) {
if (lastError == null) {
lastError = new ProgramException("Interrupt signalled by BRK opcode", lastBreakpoint);
}
programReportedError=true;
}
if (programReportedError) {
throw lastError;
}

View File

@ -20,14 +20,22 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import jace.Emulator;
import jace.ProgramException;
import jace.TestProgram;
import jace.apple2e.MOS65C02;
import jace.apple2e.RAM128k;
import jace.apple2e.SoftSwitches;
/**
* Test that memory listeners fire appropriately.
@ -37,14 +45,36 @@ public class MemoryTest {
static Computer computer;
static MOS65C02 cpu;
static RAM128k ram;
static String MEMORY_TEST_COMMONS;
static String MACHINE_IDENTIFICATION;
@BeforeClass
public static void setupClass() {
public static void setupClass() throws IOException, URISyntaxException {
initComputer();
SoundMixer.MUTE = true;
computer = Emulator.withComputer(c->c, null);
cpu = (MOS65C02) computer.getCpu();
ram = (RAM128k) computer.getMemory();
ram.addExecutionTrap("COUT intercept", 0x0FDF0, (e)->{
char c = (char) (cpu.A & 0x07f);
if (c == '\r') {
System.out.println();
} else {
System.out.print(c);
}
});
MEMORY_TEST_COMMONS = Files.readString(Paths.get(MemoryTest.class.getResource("/jace/memory_test_commons.asm").toURI()));
MACHINE_IDENTIFICATION = Files.readString(Paths.get(MemoryTest.class.getResource("/jace/machine_identification.asm").toURI()));
}
@Before
public void setup() {
computer.pause();
cpu.clearState();
// Reset softswitches
for (SoftSwitches softswitch : SoftSwitches.values()) {
softswitch.getSwitch().reset();
}
}
@Test
@ -152,5 +182,324 @@ public class MemoryTest {
assertEquals("Should have no writes for 0x0100", 0, writeEventCaught.get());
assertEquals("Should have read event for 0x0100", 1, readAnyEventCaught.get());
assertEquals("Should have execute for 0x0100", 1, executeEventCaught.get());
}
}
/**
* Adapted version of the Apple II Family Identification Program from:
* http://www.1000bit.it/support/manuali/apple/technotes/misc/tn.misc.02.html00
*
* Adapted to ACME by Zellyn Hunter
*
* @throws ProgramException
*/
@Test
public void machineIdentificationTEst() throws ProgramException {
TestProgram memoryDetectTestProgram = new TestProgram(MEMORY_TEST_COMMONS);
memoryDetectTestProgram.add(MACHINE_IDENTIFICATION);
// Assert this is an Apple //e
memoryDetectTestProgram.assertAddrVal(0x0800, 0x04);
// Assert this is an enhanced revision
memoryDetectTestProgram.assertAddrVal(0x0801, 0x02);
// Aser this is a 128k machine
memoryDetectTestProgram.assertAddrVal(0x0802, 128);
memoryDetectTestProgram.run();
}
/*
* Adapted from Zellyn Hunder's language card test:
* https://github.com/zellyn/a2audit/blob/main/audit/langcard.asm
*
* Adjusted to use JACE hooks to perform assertions and error reporting
*/
@Test
public void languageCardBankswitchTest() throws ProgramException {
TestProgram lcTestProgram = new TestProgram(MEMORY_TEST_COMMONS);
lcTestProgram.add("""
;; Setup - store differing values in bank first and second banked areas.
lda $C08B ; Read and write bank 1
lda $C08B
lda #$11
sta $D17B ; $D17B is $53 in Apple II/plus/e/enhanced
cmp $D17B
""")
.assertEquals("E0004: We tried to put the language card into read bank 1, write bank 1, but failed to write.")
.add("""
lda #$33
sta $FE1F ; FE1F is $60 in Apple II/plus/e/enhanced
cmp $FE1F
""")
//
.assertEquals("E0005: We tried to put the language card into read RAM, write RAM, but failed to write.")
.add("""
lda $C083 ; Read and write bank 2
lda $C083
lda #$22
sta $D17B
cmp $D17B
""")
.assertEquals("E0006: We tried to put the language card into read bank 2, write bank 2, but failed to write.")
.add("""
lda $C08B ; Read and write bank 1 with single access (only one needed if banked in already)
lda #$11
cmp $D17B
""")
.assertEquals("E000D: We tried to put the language card into read bank 1, but failed to read.")
.add("""
lda $C081 ; Read ROM with single access (only one needed to bank out)
lda #$53
cmp $D17B
""")
.assertEquals("E000E: We tried to put the language card into read ROM, but failed to read (from ROM).")
.add("""
;;; Main data-driven test. PCL,PCH holds the address of the next
;;; data-driven test routine. We expect the various softswitches
;;; to be reset each time we loop at .ddloop.
.datadriventests
lda #<.tests
sta PCL
lda #>.tests
sta PCH
;;; Main data-drive-test loop.
.ddloop
ldy #0
;; Initialize to known state:
;; - $11 in $D17B bank 1 (ROM: $53)
;; - $22 in $D17B bank 2 (ROM: $53)
;; - $33 in $FE1F (ROM: $60)
lda $C08B ; Read and write bank 1
lda $C08B
lda #$11
sta $D17B
lda #$33
sta $FE1F
lda $C083 ; Read and write bank 2
lda $C083
lda #$22
sta $D17B
lda $C080
jmp (PCL) ; Jump to test routine
;; Test routine will JSR back to here, so the check data address is on the stack.
.test ;; ... test the quintiple of test values
inc $D17B
inc $FE1F
;; pull address off of stack: it points just below check data for this test.
pla
sta .checkdata
pla
sta .checkdata+1
;; .checkdata now points to d17b-current,fe1f-current,bank1,bank2,fe1f-ram test quintiple
;; Test current $D17B
jsr NEXTCHECK
cmp $D17B
beq +
lda $D17B
pha
jsr .printseq
+print
!text "$D17B TO CONTAIN $"
+printed
jsr CURCHECK
jsr PRBYTE
+print
!text ", GOT $"
+printed
pla
jsr PRBYTE
lda #$8D
jsr COUT
jmp .datatesturl
+ ;; Test current $FE1F
jsr NEXTCHECK
cmp $FE1F
beq +
lda $FE1F
pha
jsr .printseq
+print
!text "$FE1F=$"
+printed
jsr CURCHECK
jsr PRBYTE
+print
!text ", GOT $"
+printed
pla
jsr PRBYTE
lda #$8D
jsr COUT
jmp .datatesturl
+ ;; Test bank 1 $D17B
lda $C088
jsr NEXTCHECK
cmp $D17B
beq +
lda $D17B
pha
jsr .printseq
+print
!text "$D17B IN RAM BANK 1 TO CONTAIN $"
+printed
jsr CURCHECK
jsr PRBYTE
+print
!text ", GOT $"
+printed
pla
jsr PRBYTE
lda #$8D
jsr COUT
jmp .datatesturl
+ ;; Test bank 2 $D17B
lda $C080
jsr NEXTCHECK
cmp $D17B
beq +
lda $D17B
pha
jsr .printseq
+print
!text "$D17B IN RAM BANK 2 TO CONTAIN $"
+printed
jsr CURCHECK
jsr PRBYTE
+print
!text ", GOT $"
+printed
pla
jsr PRBYTE
lda #$8D
jsr COUT
jmp .datatesturl
+ ;; Test RAM $FE1F
lda $C080
jsr NEXTCHECK
cmp $FE1F
beq +
lda $FE1F
pha
jsr .printseq
+print
!text "RAM $FE1F=$"
+printed
jsr CURCHECK
jsr PRBYTE
+print
!text ", GOT $"
+printed
pla
jsr PRBYTE
lda #$8D
jsr COUT
jmp .datatesturl
+ ;; Jump PCL,PCH up to after the test data, and loop.
jsr NEXTCHECK
bne +
+success
+ ldx .checkdata
ldy .checkdata+1
stx PCL
sty PCH
jmp .ddloop
.datatesturl
""")
.throwError("E0007: This is a data-driven test of Language Card operation. We initialize $D17B in RAM bank 1 to $11, $D17B in RAM bank 2 to $22, and $FE1F in RAM to $33. Then, we perform a testdata-driven sequence of LDA and STA to the $C08X range. Finally we (try to) increment $D17B and $FE1F. Then we test (a) the current live value in $D17B, (b) the current live value in $FE1F, (c) the RAM bank 1 value of $D17B, (d) the RAM bank 2 value of $D17B, and (e) the RAM value of $FE1F, to see whether they match expected values. $D17B is usually $53 in ROM, and $FE1F is usally $60. For more information on the operation of the language card soft-switches, see Understanding the Apple IIe, by James Fielding Sather, Pg 5-24.")
.add("""
rts
.printseq
+print
!text "AFTER SEQUENCE OF:",$8D,"- LDA $C080",$8D
+printed
jsr PRINTTEST
+print
!text "- INC $D17B",$8D,"- INC $FE1F",$8D,"EXPECTED "
+printed
rts
.tests
;; Format:
;; Sequence of test instructions, finishing with `jsr .test`.
;; - quint: expected current $d17b and fe1f, then d17b in bank1, d17b in bank 2, and fe1f
;; (All sequences start with lda $C080, just to reset things to a known state.)
;; 0-byte to terminate tests.
lda $C088 ; Read $C088 (RAM read, write protected)
jsr .test ;
!byte $11, $33, $11, $22, $33 ;
jsr .test ;
!byte $22, $33, $11, $22, $33 ;
lda $C081 ; Read $C081 (ROM read, write disabled)
jsr .test ;
!byte $53, $60, $11, $22, $33
lda $C081 ; Read $C081, $C089 (ROM read, bank 1 write)
lda $C089 ;
jsr .test ;
!byte $53, $60, $54, $22, $61
lda $C081 ; Read $C081, $C081 (read ROM, write RAM bank 2)
lda $C081 ;
jsr .test ;
!byte $53, $60, $11, $54, $61
lda $C081 ; Read $C081, $C081, write $C081 (read ROM, write RAM bank bank 2)
lda $C081 ; See https://github.com/zellyn/a2audit/issues/3
sta $C081 ;
jsr .test ;
!byte $53, $60, $11, $54, $61
lda $C081 ; Read $C081, $C081; write $C081, $C081
lda $C081 ; See https://github.com/zellyn/a2audit/issues/4
sta $C081 ;
sta $C081 ;
jsr .test ;
!byte $53, $60, $11, $54, $61
lda $C08B ; Read $C08B (read RAM bank 1, no write)
jsr .test ;
!byte $11, $33, $11, $22, $33
lda $C083 ; Read $C083 (read RAM bank 2, no write)
jsr .test ;
!byte $22, $33, $11, $22, $33
lda $C08B ; Read $C08B, $C08B (read/write RAM bank 1)
lda $C08B ;
jsr .test ;
!byte $12, $34, $12, $22, $34
lda $C08F ; Read $C08F, $C087 (read/write RAM bank 2)
lda $C087 ;
jsr .test ;
!byte $23, $34, $11, $23, $34
lda $C087 ; Read $C087, read $C08D (read ROM, write bank 1)
lda $C08D ;
jsr .test ;
!byte $53, $60, $54, $22, $61
lda $C08B ; Read $C08B, write $C08B, read $C08B (read RAM bank 1, no write)
sta $C08B ; (this one is tricky: reset WRTCOUNT by writing halfway)
lda $C08B ;
jsr .test ;
!byte $11, $33, $11, $22, $33
sta $C08B ; Write $C08B, write $C08B, read $C08B (read RAM bank 1, no write)
sta $C08B ;
lda $C08B ;
jsr .test ;
!byte $11, $33, $11, $22, $33
clc ; Read $C083, $C083 (read/write RAM bank 2)
ldx #0 ; Uses "6502 false read"
inc $C083,x ;
jsr .test ;
!byte $23, $34, $11, $23, $34
!byte 0
""")
.runForTicks(10000000);
}
}

View File

@ -0,0 +1,396 @@
JSR IDENTIFY
JMP END_DETECT_PROGRAM
;;; From http://www.1000bit.it/support/manuali/apple/technotes/misc/tn.misc.02.html
;;; *********************************************
;;; * *
;;; * 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:
IIplain = $01 ;Apple II
IIplus = $02 ;Apple II+
IIIem = $03 ;Apple /// in emulation mode
IIe = $04 ;Apple IIe
IIc = $05 ;Apple IIc
IIeCard = $06 ;Apple IIe Card for the Macintosh LC
.safe = $0001 ;start of code relocated to zp
.location = $06 ;zero page location to use
.test1 = $AA ;test byte #1
.test2 = $55 ;lsr of test1
.test3 = $88 ;test byte #3
.test4 = $EE ;test byte #4
.begpage1 = $400 ;beginning of text page 1
.begpage2 = $800 ;beginning of text page 2
.begsprse = $C00 ;byte after text page 2
.clr80col = $C000 ;disable 80-column store
.set80col = $C001 ;enable 80-column store
.rdmainram = $C002 ;read main ram
.rdcardram = $C003 ;read aux ram
.wrmainram = $C004 ;write main ram
.wrcardram = $C005 ;write aux ram
.rdramrd = $C013 ;are we reading aux ram?
.rdaltzp = $C016 ;are we reading aux zero page?
.rd80col = $C018 ;are we using 80-columns?
.rdtext = $C01A ;read if text is displayed
.rdpage2 = $C01C ;read if page 2 is displayed
.txtclr = $C050 ;switch in graphics
.txtset = $C051 ;switch in text
.txtpage1 = $C054 ;switch in page 1
.txtpage2 = $C055 ;switch in page 2
.ramin = $C080 ;read LC bank 2, write protected
.romin = $C081 ;read ROM, 2 reads write enable LC
.lcbank1 = $C08B ;LC bank 1 enable
.lc1 = $E000 ;bytes to save for LC
.lc2 = $D000 ;save/restore routine
.lc3 = $D400
.lc4 = $D800
.idroutine = $FE1F ;IIgs id routine
; Start by saving the state of the language card banks and
; by switching in main ROM.
IDENTIFY
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.
!cpu 65816 {
.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
!al { ;longa on
!rl { ;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
} ;longi off
} ;longa 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 ;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:
.IIgsA !word 0 ;16-bit field
.IIgsX !word 0 ;16-bit field
.IIgsY !word 0 ;16-bit field
.save !fill 6,0 ;six bytes for saved data
.IDTable
;dc I1'1,1' ;Apple ][
;dc H'B3 38 00'
!byte 1,1
!byte $B3,$38,0
;dc I1'2,1' ;Apple ][+
;dc H'B3 EA 1E AD 00'
!byte 2,1
!byte $B3,$EA,$1E,$AD,0
;dc I1'3,1' ;Apple /// (emulation)
;dc H'B3 EA 1E 8A 00'
!byte 3,1
!byte $B3,$EA,$1E,$8A,0
;dc I1'4,1' ;Apple IIe (original)
;dc H'B3 06 C0 EA 00'
!byte 4,1
!byte $B3,$06,$C0,$EA,0
; 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'
!byte 6,1
!byte $B3,$06,$C0,$E0,$DD,$02,$BE,$00,0
;dc I1'4,2' ;Apple IIe (enhanced)
;dc H'B3 06 C0 E0 00'
!byte 4,2
!byte $B3,$06,$C0,$E0,0
;dc I1'5,1' ;Apple IIc (original)
;dc H'B3 06 C0 00 BF FF 00'
!byte 5,1
!byte $B3,$06,$C0,$00,$BF,$FF,0
;dc I1'5,2' ;Apple IIc (3.5 ROM)
;dc H'B3 06 C0 00 BF 00 00'
!byte 5,2
!byte $B3,$06,$C0,$00,$BF,$00,0
;dc I1'5,3' ;Apple IIc (Mem. Exp)
;dc H'B3 06 C0 00 BF 03 00'
!byte 5,3
!byte $B3,$06,$C0,$00,$BF,$03,0
;dc I1'5,4' ;Apple IIc (Rev. Mem. Exp.)
;dc H'B3 06 C0 00 BF 04 00'
!byte 5,4
!byte $B3,$06,$C0,$00,$BF,$04,0
;dc I1'5,5' ;Apple IIc Plus
;dc H'B3 06 C0 00 BF 05 00'
!byte 5,5
!byte $B3,$06,$C0,$00,$BF,$05,0
;dc I1'0,0' ;end of table
!byte 0,0
END_DETECT_PROGRAM

View File

@ -0,0 +1,204 @@
jmp START
MACHINE = $800 ;the type of Apple II
ROMLEVEL = $801 ;which revision of the machine
MEMORY = $802 ;how much memory (up to 128K)
LCRESULT = $10
LCRESULT1 = $11
lda #0
sta LCRESULT
;; Zero-page locations.
SCRATCH = $1
SCRATCH2 = $2
SCRATCH3 = $3
LCRESULT = $10
LCRESULT1 = $11
AUXRESULT = $12
SOFTSWITCHRESULT = $13
CSW = $36
KSW = $38
PCL=$3A
PCH=$3B
A1L=$3C
A1H=$3D
A2L=$3E
A2H=$3F
A3L=$40
A3H=$41
A4L=$42
A4H=$43
!addr tmp0 = $f9
!addr tmp1 = $fa
!addr tmp2 = $fb
!addr tmp3 = $fc
!addr tmp4 = $fd
!addr tmp5 = $fe
!addr tmp6 = $ff
.checkdata = tmp1
STRINGS = $8000
!set LASTSTRING = STRINGS
KBD = $C000
KBDSTRB = $C010
;; Monitor locations.
;HOME = $FC58
COUT = $FDED
COUT1 = $FDF0
KEYIN = $FD1B
CROUT = $FD8E
PRBYTE = $FDDA
PRNTYX = $F940
;; Softswitch locations.
RESET_80STORE = $C000
SET_80STORE = $C001
READ_80STORE = $C018
RESET_RAMRD = $C002
SET_RAMRD = $C003
READ_RAMRD = $C013
RESET_RAMWRT = $C004
SET_RAMWRT = $C005
READ_RAMWRT = $C014
RESET_INTCXROM = $C006
SET_INTCXROM = $C007
READ_INTCXROM = $C015
RESET_ALTZP = $C008
SET_ALTZP = $C009
READ_ALTZP = $C016
RESET_SLOTC3ROM = $C00A
SET_SLOTC3ROM = $C00B
READ_SLOTC3ROM = $C017
RESET_80COL = $C00C
SET_80COL = $C00D
READ_80COL = $C01F
RESET_ALTCHRSET = $C00E
SET_ALTCHRSET = $C00F
READ_ALTCHRSET = $C01E
RESET_TEXT = $C050
SET_TEXT = $C051
READ_TEXT = $C01A
RESET_MIXED = $C052
SET_MIXED = $C053
READ_MIXED = $C01B
RESET_PAGE2 = $C054
SET_PAGE2 = $C055
READ_PAGE2 = $C01C
RESET_HIRES = $C056
SET_HIRES = $C057
READ_HIRES = $C01D
RESET_AN3 = $C05E
SET_AN3 = $C05F
RESET_INTC8ROM = $CFFF
;; Readable things without corresponding set/reset pairs.
READ_HRAM_BANK2 = $C011
READ_HRAMRD = $C012
READ_VBL = $C019
print
lda $C081
lda $C081
pla
sta getch+1
pla
sta getch+2
- inc getch+1
bne getch
inc getch+2
getch lda $FEED ; FEED gets modified
beq +
jsr COUT
jmp -
+ rts
PRINTTEST
-
ldy #0
lda (PCL),y
cmp #$20
beq +++
lda #'-'
jsr COUT
lda #' '
jsr COUT
ldx #0
lda (PCL,x)
jsr $f88e
ldx #3
jsr $f8ea
jsr $f953
sta PCL
sty PCH
lda #$8D
jsr COUT
jmp -
+++ rts
;;; Increment .checkdata pointer to the next memory location, and load
;;; it into the accumulator. X and Y are preserved.
NEXTCHECK
inc .checkdata
bne CURCHECK
inc .checkdata+1
CURCHECK
sty SCRATCH
ldy #0
lda (.checkdata),y
ldy SCRATCH
ora #0
rts
!macro print {
jsr LASTSTRING
!set TEMP = *
* = LASTSTRING
jsr print
}
!macro printed {
!byte 0
!set LASTSTRING=*
* = TEMP
}
START
;;; Reset all soft-switches to known-good state. Burns $300 and $301 in main mem.
RESETALL
; The COUT hook isn't set up yet, so the monitor routine will crash unless we set it up
lda #<COUT1
sta CSW
lda #>COUT1
sta CSW+1
sta RESET_RAMRD
sta RESET_RAMWRT
;; Save return address in X and A, in case we switch zero-page memory.
sta RESET_80STORE
sta RESET_INTCXROM
sta RESET_ALTZP
sta RESET_SLOTC3ROM
sta RESET_INTC8ROM
sta RESET_80COL
sta RESET_ALTCHRSET
sta SET_TEXT
sta RESET_MIXED
sta RESET_PAGE2
sta RESET_HIRES