Card to bridge PR# and IN# to the host terminal
This commit is contained in:
parent
89881a8466
commit
8b50ae1500
|
@ -24,11 +24,14 @@ Portable emulator of an Apple II+ or //e. Written in Go.
|
||||||
- 1Mb Memory Expansion Card (slinky)
|
- 1Mb Memory Expansion Card (slinky)
|
||||||
- RAMWorks style expansion Card (up to 16MB additional) (Apple //e only)
|
- RAMWorks style expansion Card (up to 16MB additional) (Apple //e only)
|
||||||
- ThunderClock Plus real time clock
|
- ThunderClock Plus real time clock
|
||||||
- Bootable Smartport / ProDOS card
|
|
||||||
- Apple //e 80 columns with 64Kb extra RAM and optional RGB modes
|
- Apple //e 80 columns with 64Kb extra RAM and optional RGB modes
|
||||||
|
- No Slot Clock based on the DS1216
|
||||||
|
- Useful cards not emulating a real card
|
||||||
|
- Bootable Smartport / ProDOS card
|
||||||
- VidHd, limited to the ROM signature and SHR as used by Total Replay, only for //e models with 128Kb
|
- VidHd, limited to the ROM signature and SHR as used by Total Replay, only for //e models with 128Kb
|
||||||
- FASTChip, limited to what Total Replay needs to set and clear fast mode
|
- FASTChip, limited to what Total Replay needs to set and clear fast mode
|
||||||
- No Slot Clock based on the DS1216
|
- Host console card. Maps the host STDIN and STDOUT to PR# and IN#
|
||||||
|
|
||||||
- Graphic modes:
|
- Graphic modes:
|
||||||
- Text 40 columns
|
- Text 40 columns
|
||||||
- Text 80 columns (Apple //e only)
|
- Text 80 columns (Apple //e only)
|
||||||
|
@ -164,6 +167,8 @@ Only valid on SDL mode
|
||||||
```terminal
|
```terminal
|
||||||
-charRom string
|
-charRom string
|
||||||
rom file for the character generator (default "<default>")
|
rom file for the character generator (default "<default>")
|
||||||
|
-consoleCardSlot int
|
||||||
|
slot for the host console card. -1 for none (default -1)
|
||||||
-disk string
|
-disk string
|
||||||
file to load on the first disk drive (default "<internal>/dos33.dsk")
|
file to load on the first disk drive (default "<internal>/dos33.dsk")
|
||||||
-disk2Slot int
|
-disk2Slot int
|
||||||
|
|
|
@ -149,10 +149,8 @@ func (a *Apple2) AddMemoryExpansionCard(slot int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddThunderClockPlusCard inserts a ThunderClock Plus clock card
|
// AddThunderClockPlusCard inserts a ThunderClock Plus clock card
|
||||||
func (a *Apple2) AddThunderClockPlusCard(slot int, romFile string) error {
|
func (a *Apple2) AddThunderClockPlusCard(slot int) {
|
||||||
c := NewCardThunderClockPlus()
|
a.insertCard(NewCardThunderClockPlus(), slot)
|
||||||
a.insertCard(c, slot)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddRGBCard inserts an RBG option to the Apple IIe 80 col 64KB card
|
// AddRGBCard inserts an RBG option to the Apple IIe 80 col 64KB card
|
||||||
|
|
|
@ -75,6 +75,10 @@ func MainApple() *Apple2 {
|
||||||
"thunderClockCardSlot",
|
"thunderClockCardSlot",
|
||||||
4,
|
4,
|
||||||
"slot for the ThunderClock Plus card. -1 for none")
|
"slot for the ThunderClock Plus card. -1 for none")
|
||||||
|
consoleCardSlot := flag.Int(
|
||||||
|
"consoleCardSlot",
|
||||||
|
-1,
|
||||||
|
"slot for the host console card. -1 for none")
|
||||||
nsc := flag.Int(
|
nsc := flag.Int(
|
||||||
"nsc",
|
"nsc",
|
||||||
-1,
|
-1,
|
||||||
|
@ -235,15 +239,14 @@ func MainApple() *Apple2 {
|
||||||
a.AddMemoryExpansionCard(*memoryExpansionCardSlot)
|
a.AddMemoryExpansionCard(*memoryExpansionCardSlot)
|
||||||
}
|
}
|
||||||
if *thunderClockCardSlot > 0 {
|
if *thunderClockCardSlot > 0 {
|
||||||
err := a.AddThunderClockPlusCard(*thunderClockCardSlot,
|
a.AddThunderClockPlusCard(*thunderClockCardSlot)
|
||||||
"<internal>/ThunderclockPlusROM.bin")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if *vidHDCardSlot >= 0 {
|
if *vidHDCardSlot >= 0 {
|
||||||
a.AddVidHD(*vidHDCardSlot)
|
a.AddVidHD(*vidHDCardSlot)
|
||||||
}
|
}
|
||||||
|
if *consoleCardSlot >= 0 {
|
||||||
|
a.AddCardInOut(*consoleCardSlot)
|
||||||
|
}
|
||||||
|
|
||||||
if *smartPortImage != "" {
|
if *smartPortImage != "" {
|
||||||
err := a.AddSmartPortDisk(5, *smartPortImage, *traceHD)
|
err := a.AddSmartPortDisk(5, *smartPortImage, *traceHD)
|
||||||
|
@ -294,7 +297,6 @@ func MainApple() *Apple2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// a.AddRomX()
|
// a.AddRomX()
|
||||||
// a.AddCardInOut(2)
|
|
||||||
// a.AddCardLogger(4)
|
// a.AddCardLogger(4)
|
||||||
|
|
||||||
if *dumpChars {
|
if *dumpChars {
|
||||||
|
|
197
cardInOut.go
197
cardInOut.go
|
@ -1,7 +1,9 @@
|
||||||
package izapple2
|
package izapple2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -11,12 +13,15 @@ See:
|
||||||
"Apple II Monitors peeled."
|
"Apple II Monitors peeled."
|
||||||
http://mysite.du.edu/~etuttle/math/acia.htm
|
http://mysite.du.edu/~etuttle/math/acia.htm
|
||||||
|
|
||||||
|
|
||||||
|
PR#n stores Cn00 in CSWL and CSWH
|
||||||
|
IN#n stores Cn00 in KSWL and KSWH
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// CardInOut is an experimental card to bridge with the host
|
// CardInOut is an experimental card to bridge with the host console
|
||||||
type CardInOut struct {
|
type CardInOut struct {
|
||||||
cardBase
|
cardBase
|
||||||
i int
|
reader *bufio.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCardInOut creates CardInOut
|
// NewCardInOut creates CardInOut
|
||||||
|
@ -27,129 +32,137 @@ func NewCardInOut() *CardInOut {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CardInOut) assign(a *Apple2, slot int) {
|
func (c *CardInOut) assign(a *Apple2, slot int) {
|
||||||
for i := uint8(0x0); i <= 0xf; i++ {
|
c.addCardSoftSwitchR(0, func(*ioC0Page) uint8 {
|
||||||
iCopy := i
|
if c.reader == nil {
|
||||||
c.addCardSoftSwitchR(i, func(*ioC0Page) uint8 {
|
c.reader = bufio.NewReader(os.Stdin)
|
||||||
value := []uint8{0xc1, 0xc1, 0x93, 0x0}[c.i%4]
|
}
|
||||||
c.i++
|
value, err := c.reader.ReadByte()
|
||||||
fmt.Printf("[cardInOut] Read access to softswith 0x%x for slot %v, value %x.\n", iCopy, slot, value)
|
|
||||||
//return 0x41 + 0x80
|
|
||||||
return []uint8{0x41, 0x41, 0x13}[i%3] + 0x80
|
|
||||||
}, "INOUTR")
|
|
||||||
c.addCardSoftSwitchW(i, func(_ *ioC0Page, value uint8) {
|
|
||||||
fmt.Printf("[cardInOut] Write access to softswith 0x%x for slot %v, value 0x%x.\n", iCopy, slot, value)
|
|
||||||
}, "INOUTW")
|
|
||||||
}
|
|
||||||
|
|
||||||
in := true
|
if err != nil {
|
||||||
out := false
|
panic(err)
|
||||||
|
}
|
||||||
|
value += 0x80
|
||||||
|
if value&0x7f == 10 {
|
||||||
|
value = 13 + 0x80
|
||||||
|
}
|
||||||
|
//fmt.Printf("[cardInOut] Read access to softswith 0x%x for slot %v, value %x.\n", 0, slot, value)
|
||||||
|
return value
|
||||||
|
}, "INOUTR")
|
||||||
|
c.addCardSoftSwitchW(1, func(_ *ioC0Page, value uint8) {
|
||||||
|
//fmt.Printf("[cardInOut] Write access to softswith 0x%x for slot %v, value 0x%x: %v, %v.\n", 1, slot, value, value&0x7f, string(value&0x7f))
|
||||||
|
if value&0x7f == 13 {
|
||||||
|
fmt.Printf("\n")
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%v", string(value&0x7f))
|
||||||
|
}
|
||||||
|
|
||||||
|
}, "INOUTW")
|
||||||
|
|
||||||
data := [256]uint8{
|
data := [256]uint8{
|
||||||
// Register
|
// Register
|
||||||
0xA9, 0xC2,
|
0x4c, 0x40, 0xc2, 0, 0, 0, 0, 0,
|
||||||
0x85, 0x37,
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0x85, 0x39,
|
|
||||||
0xA9, 0x10,
|
|
||||||
0x85, 0x36,
|
|
||||||
0xA9, 0x15,
|
|
||||||
0x85, 0x38,
|
|
||||||
0x60, 0xEA,
|
|
||||||
|
|
||||||
// Out char
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0x8D, 0xA1, 0xC0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0x60, 0xEA,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
|
||||||
// Get char
|
0x48, 0xA5, 0x38, 0xD0, 0x12, 0xA9, 0xC2, 0xC5,
|
||||||
0x91, 0x28,
|
0x39, 0xD0, 0x0C, 0xAD, 0x50, 0x00, 0x85, 0x38,
|
||||||
0xAD, 0xA0, 0xC0,
|
0x68, 0x91, 0x28, 0xAD, 0xA0, 0xC0, 0x60, 0x68,
|
||||||
0x60,
|
0x8D, 0xA1, 0xC0, 0x60,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !out {
|
c.romCsxx = newMemoryRangeROM(0xC200, data[:], "InOUt card")
|
||||||
// NOP the CSWL,H change
|
|
||||||
for _, v := range []uint8{2, 3, 8, 9} {
|
|
||||||
data[v] = 0xEA
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !in {
|
// Fix slot dependant addresses
|
||||||
// NOP the KSWL,H change
|
data[0x02] = uint8(0xc0 + slot)
|
||||||
for _, v := range []uint8{4, 5, 12, 13} {
|
data[0x46] = uint8(0xc0 + slot)
|
||||||
data[v] = 0xEA
|
data[0x54] = uint8(0x80 + slot<<4)
|
||||||
}
|
data[0x59] = uint8(0x81 + slot<<4)
|
||||||
}
|
|
||||||
|
|
||||||
c.romCsxx = newMemoryRangeROM(0xC200, data[0:255], "InOUt card")
|
|
||||||
|
|
||||||
if slot != 2 {
|
|
||||||
// To make it work on other slots, patch C2, A0 and A1
|
|
||||||
panic("Assert failed. Only slot 2 supported for the InOut card")
|
|
||||||
}
|
|
||||||
c.cardBase.assign(a, slot)
|
c.cardBase.assign(a, slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The ROM code was assembled using https://www.masswerk.at/6502/assembler.html
|
The ROM code was assembled using https://www.masswerk.at/6502/assembler.html
|
||||||
|
|
||||||
|
We will have $Cn00 as the entry point for CSWL/H. But before doing
|
||||||
|
anything we have to check that we are not in $Cn00 because of an IN#.
|
||||||
|
To da that we check id KSWL/H is $Cn00, it is it we wif it to INEntry.
|
||||||
|
|
||||||
src:
|
src:
|
||||||
BASL = $28
|
BASL = $28
|
||||||
CSWL = $36
|
|
||||||
CSWH = $37
|
|
||||||
KSWL = $38
|
KSWL = $38
|
||||||
KSWH = $39
|
KSWH = $39
|
||||||
|
|
||||||
* = $C200
|
* = $C200
|
||||||
Register:
|
Entry:
|
||||||
|
JMP SkipHeader
|
||||||
|
|
||||||
|
* = $C240
|
||||||
|
SkipHeader:
|
||||||
|
PHA
|
||||||
|
LDA *KSWL
|
||||||
|
BNE PREntry
|
||||||
LDA #$C2
|
LDA #$C2
|
||||||
STA *CSWH
|
CMP *KSWH
|
||||||
STA *KSWH
|
BNE PREntry
|
||||||
LDA #$10
|
FixKSWL:
|
||||||
STA *CSWL
|
LDA <INEntry
|
||||||
LDA #$15
|
|
||||||
STA *KSWL
|
STA *KSWL
|
||||||
RTS
|
INEntry:
|
||||||
NOP
|
PLA
|
||||||
OutChar:
|
|
||||||
STA $C0A1
|
|
||||||
RTS
|
|
||||||
NOP
|
|
||||||
GetChar:
|
|
||||||
STA (BASL),Y
|
STA (BASL),Y
|
||||||
LDA $C0A0
|
LDA $C0A0
|
||||||
RTS
|
RTS
|
||||||
|
PREntry:
|
||||||
|
PLA
|
||||||
|
STA $C0A1
|
||||||
|
RTS
|
||||||
|
|
||||||
|
|
||||||
assembled as:
|
|
||||||
|
Listing:
|
||||||
|
pass 2
|
||||||
|
|
||||||
0000 BASL = 0028
|
0000 BASL = 0028
|
||||||
0000 CSWL = 0036
|
|
||||||
0000 CSWH = 0037
|
|
||||||
0000 KSWL = 0038
|
0000 KSWL = 0038
|
||||||
0000 KSWH = 0039
|
0000 KSWH = 0039
|
||||||
|
|
||||||
* = $C200
|
* = $C200
|
||||||
C200 REGIST
|
C200 ENTRY:
|
||||||
C200 LDA #$C2 A9 C2
|
C200 JMP SKIPHE 4C 40 C2
|
||||||
C202 STA *CSWH 85 37
|
|
||||||
C204 STA *KSWH 85 39
|
|
||||||
C206 LDA #$10 A9 10
|
|
||||||
C208 STA *CSWL 85 36
|
|
||||||
C20A LDA #$15 A9 15
|
|
||||||
C20C STA *KSWL 85 38
|
|
||||||
C20E RTS 60
|
|
||||||
C20F NOP EA
|
|
||||||
C210 OUTCHA
|
|
||||||
C210 STA $C0A1 8D A1 C0
|
|
||||||
C213 RTS 60
|
|
||||||
C214 NOP EA
|
|
||||||
C215 GETCHA
|
|
||||||
C215 STA (BASL),Y 91 28
|
|
||||||
C217 LDA $C0A0 AD A0 C0
|
|
||||||
C21A RTS
|
|
||||||
|
|
||||||
object code:
|
* = $C240
|
||||||
A9 C2 85 37 85 39 A9 10
|
C240 SKIPHE
|
||||||
85 36 A9 15 85 38 60 EA
|
C240 PHA 48
|
||||||
8D A1 C0 60 EA 91 28 AD
|
C241 LDA *KSWL A5 38
|
||||||
A0 C0 60
|
C243 BNE PRENTR D0 12
|
||||||
|
C245 LDA #$C2 A9 C2
|
||||||
|
C247 CMP *KSWH C5 39
|
||||||
|
C249 BNE PRENTR D0 0C
|
||||||
|
C24B FIXKSW
|
||||||
|
C24B LDA <INENTR AD 50 00
|
||||||
|
C24E STA *KSWL 85 38
|
||||||
|
C250 INENTR
|
||||||
|
C250 PLA 68
|
||||||
|
C251 STA (BASL),Y 91 28
|
||||||
|
C253 LDA $C0A0 AD A0 C0
|
||||||
|
C256 RTS 60
|
||||||
|
C257 PRENTR
|
||||||
|
C257 PLA 68
|
||||||
|
C258 STA $C0A1 8D A1 C0
|
||||||
|
C25B RTS 60
|
||||||
|
|
||||||
|
done.
|
||||||
|
|
||||||
|
Object Code:
|
||||||
|
c200:
|
||||||
|
4C 40 C2
|
||||||
|
c240:
|
||||||
|
48 A5 38 D0 12
|
||||||
|
A9 C2 C5 39 D0 0C AD 50
|
||||||
|
00 85 38 68 91 28 AD A0
|
||||||
|
C0 60 68 8D A1 C0 60
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue