mirror of
https://github.com/ivanizag/izapple2.git
synced 2024-06-05 02:29:29 +00:00
Support for text mode rendering
This commit is contained in:
parent
016e1ea875
commit
2cd3ed3a48
|
@ -89,7 +89,7 @@ func ansiCursorUp(steps int) {
|
|||
fmt.Printf("\033[%vA", steps)
|
||||
}
|
||||
|
||||
func (fe *ansiConsoleFrontend) textModeGoRoutine() {
|
||||
func (fe *ansiConsoleFrontend) textModeGoRoutineFast() {
|
||||
fe.extraLineFeeds = make(chan int, 100)
|
||||
|
||||
fmt.Printf(strings.Repeat("\n", 26))
|
||||
|
@ -112,7 +112,7 @@ func (fe *ansiConsoleFrontend) textModeGoRoutine() {
|
|||
|
||||
// See "Understand the Apple II", page 5-10
|
||||
// http://www.applelogic.org/files/UNDERSTANDINGTHEAII.pdf
|
||||
isAltText := fe.apple2.isApple2e && fe.apple2.io.isSoftSwitchExtActive(ioFlagAltChar)
|
||||
isAltText := fe.apple2.isApple2e && fe.apple2.io.isSoftSwitchActive(ioFlagAltChar)
|
||||
var i, j, h, c uint8
|
||||
// Top, middle and botton screen
|
||||
for i = 0; i < 120; i = i + 40 {
|
||||
|
@ -139,6 +139,52 @@ func (fe *ansiConsoleFrontend) textModeGoRoutine() {
|
|||
}
|
||||
}
|
||||
|
||||
func (fe *ansiConsoleFrontend) textModeGoRoutine() {
|
||||
fe.extraLineFeeds = make(chan int, 100)
|
||||
|
||||
fmt.Printf(strings.Repeat("\n", 26))
|
||||
for {
|
||||
if fe.textUpdated {
|
||||
fe.textUpdated = false
|
||||
// Go up
|
||||
ansiCursorUp(26)
|
||||
done := false
|
||||
for !done {
|
||||
select {
|
||||
case lineFeeds := <-fe.extraLineFeeds:
|
||||
ansiCursorUp(lineFeeds)
|
||||
default:
|
||||
done = true
|
||||
}
|
||||
}
|
||||
|
||||
pageIndex := 0
|
||||
if fe.apple2.io.isSoftSwitchActive(ioFlagSecondPage) {
|
||||
pageIndex = 1
|
||||
}
|
||||
isAltText := fe.apple2.isApple2e && fe.apple2.io.isSoftSwitchActive(ioFlagAltChar)
|
||||
|
||||
fmt.Println(strings.Repeat("#", 44))
|
||||
for line := 0; line < 24; line++ {
|
||||
text := ""
|
||||
for col := 0; col < 40; col++ {
|
||||
value := getTextChar(fe.apple2, col, line, pageIndex)
|
||||
text += textMemoryByteToString(value, isAltText)
|
||||
}
|
||||
fmt.Printf("# %v #\n", text)
|
||||
}
|
||||
|
||||
fmt.Println(strings.Repeat("#", 44))
|
||||
if fe.stdinKeyboard {
|
||||
fmt.Print("\033[KLine: ")
|
||||
}
|
||||
|
||||
saveSnapshot(fe.apple2)
|
||||
}
|
||||
time.Sleep(refreshDelayMs * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func textMemoryByteToString(value uint8, isAltCharSet bool) string {
|
||||
// See https://en.wikipedia.org/wiki/Apple_II_character_set
|
||||
// Supports the new lowercase characters in the Apple2e
|
||||
|
|
|
@ -11,6 +11,7 @@ type Apple2 struct {
|
|||
cpu *core6502.State
|
||||
mmu *memoryManager
|
||||
io *ioC0Page
|
||||
cg *CharacterGenerator
|
||||
cards []cardBase
|
||||
isApple2e bool
|
||||
panicSS bool
|
||||
|
@ -18,11 +19,14 @@ type Apple2 struct {
|
|||
}
|
||||
|
||||
// NewApple2 instantiates an apple2
|
||||
func NewApple2(romFile string, panicSS bool) *Apple2 {
|
||||
func NewApple2(romFile string, charRomFile string, panicSS bool) *Apple2 {
|
||||
var a Apple2
|
||||
a.mmu = newMemoryManager(&a)
|
||||
a.cpu = core6502.NewNMOS6502(a.mmu)
|
||||
a.loadRom(romFile)
|
||||
if charRomFile != "" {
|
||||
a.cg = NewCharacterGenerator(charRomFile)
|
||||
}
|
||||
a.mmu.resetPaging()
|
||||
a.panicSS = panicSS
|
||||
|
||||
|
|
|
@ -48,7 +48,13 @@ func (cg *CharacterGenerator) load(filename string) {
|
|||
buf.Read(cg.data)
|
||||
}
|
||||
|
||||
func (cg *CharacterGenerator) dumpChar(char uint8) {
|
||||
func (cg *CharacterGenerator) getPixel(char uint8, row int, column int) bool {
|
||||
bits := cg.data[int(char)*8+row]
|
||||
bit := bits >> (uint(6 - column)) & 1
|
||||
return bit == 1
|
||||
}
|
||||
|
||||
func (cg *CharacterGenerator) dumpCharFast(char uint8) {
|
||||
base := int(char) * 8
|
||||
fmt.Printf("Char: %v\n---------\n", char)
|
||||
for i := 0; i < 8; i++ {
|
||||
|
@ -66,6 +72,23 @@ func (cg *CharacterGenerator) dumpChar(char uint8) {
|
|||
fmt.Println("---------")
|
||||
}
|
||||
|
||||
func (cg *CharacterGenerator) dumpChar(char uint8) {
|
||||
fmt.Printf("Char: %v\n---------\n", char)
|
||||
for row := 0; row < 8; row++ {
|
||||
fmt.Print("|")
|
||||
for col := 0; col < 7; col++ {
|
||||
if cg.getPixel(char, row, col) {
|
||||
fmt.Print("#")
|
||||
} else {
|
||||
fmt.Print(" ")
|
||||
}
|
||||
}
|
||||
fmt.Println("|")
|
||||
}
|
||||
fmt.Println("---------")
|
||||
}
|
||||
|
||||
// Dump to sdtout all the character maps
|
||||
func (cg *CharacterGenerator) Dump() {
|
||||
for i := 0; i < 256; i++ {
|
||||
cg.dumpChar(uint8(i))
|
||||
|
|
|
@ -15,7 +15,7 @@ type ioC0Page struct {
|
|||
type softSwitchR func(io *ioC0Page) uint8
|
||||
type softSwitchW func(io *ioC0Page, value uint8)
|
||||
|
||||
// KeyboardProvider declares the keyboard implementation requitements
|
||||
// KeyboardProvider declares the keyboard implementation requirements
|
||||
type KeyboardProvider interface {
|
||||
GetKey(strobe bool) (key uint8, ok bool)
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ func (p *ioC0Page) addSoftSwitchW(address uint8, ss softSwitchW) {
|
|||
p.softSwitchesW[address] = ss
|
||||
}
|
||||
|
||||
func (p *ioC0Page) isSoftSwitchExtActive(ioFlag uint8) bool {
|
||||
func (p *ioC0Page) isSoftSwitchActive(ioFlag uint8) bool {
|
||||
return (p.softSwitchesData[ioFlag] & ssOn) == ssOn
|
||||
}
|
||||
|
||||
|
@ -81,6 +81,10 @@ func (p *ioC0Page) Peek(address uint8) uint8 {
|
|||
return ss(p)
|
||||
}
|
||||
|
||||
func (p *ioC0Page) internalPeek(address uint8) uint8 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (p *ioC0Page) Poke(address uint8, value uint8) {
|
||||
//fmt.Printf("Poke on $C0%02x with %02x ", address, value)
|
||||
ss := p.softSwitchesW[address]
|
||||
|
|
|
@ -19,6 +19,7 @@ type memoryManager struct {
|
|||
type memoryPage interface {
|
||||
Peek(uint8) uint8
|
||||
Poke(uint8, uint8)
|
||||
internalPeek(uint8) uint8
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -36,6 +37,12 @@ func (mmu *memoryManager) Peek(address uint16) uint8 {
|
|||
return mmu.activeMemory[hi].Peek(lo)
|
||||
}
|
||||
|
||||
func (mmu *memoryManager) internalPeek(address uint16) uint8 {
|
||||
hi := uint8(address >> 8)
|
||||
lo := uint8(address)
|
||||
return mmu.activeMemory[hi].internalPeek(lo)
|
||||
}
|
||||
|
||||
// Poke sets the data at the given address
|
||||
func (mmu *memoryManager) Poke(address uint16, value uint8) {
|
||||
if address == ioC8Off {
|
||||
|
@ -55,7 +62,7 @@ func (mmu *memoryManager) setPage(index uint8, page memoryPage) {
|
|||
|
||||
// When 0xcfff is accessed the card expansion rom is unassigned
|
||||
func (mmu *memoryManager) resetSlotExpansionRoms() {
|
||||
if mmu.apple2.io.isSoftSwitchExtActive(ioFlagIntCxRom) {
|
||||
if mmu.apple2.io.isSoftSwitchActive(ioFlagIntCxRom) {
|
||||
// Ignore if the Apple2 shadow ROM is active
|
||||
return
|
||||
}
|
||||
|
|
94
apple2/screen.go
Normal file
94
apple2/screen.go
Normal file
|
@ -0,0 +1,94 @@
|
|||
package apple2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"os"
|
||||
)
|
||||
|
||||
func snapshot(a *Apple2) image.Image {
|
||||
isTextMode := a.io.isSoftSwitchActive(ioFlagGraphics)
|
||||
is80ColMode := a.io.isSoftSwitchActive(ioFlag80Col)
|
||||
pageIndex := 0
|
||||
if a.io.isSoftSwitchActive(ioFlagSecondPage) {
|
||||
pageIndex = 1
|
||||
}
|
||||
|
||||
if isTextMode && !is80ColMode {
|
||||
//Text mode
|
||||
return snapshotTextMode(a, pageIndex)
|
||||
}
|
||||
fmt.Printf("t: %v, 8: %v\n", isTextMode, is80ColMode)
|
||||
return nil
|
||||
//panic("Screen mode not supported")
|
||||
}
|
||||
|
||||
func saveSnapshot(a *Apple2) {
|
||||
img := snapshot(a)
|
||||
if img == nil {
|
||||
return
|
||||
}
|
||||
|
||||
f, err := os.Create("snapshot.png")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fmt.Println("Saving snapshot")
|
||||
|
||||
png.Encode(f, img)
|
||||
}
|
||||
|
||||
const (
|
||||
charWidth = 7
|
||||
charHeight = 8
|
||||
textColumns = 40
|
||||
textLines = 24
|
||||
textPage1Address = uint16(0x400)
|
||||
textPage2Address = uint16(0x400)
|
||||
)
|
||||
|
||||
func getTextChar(a *Apple2, col int, line int, page int) uint8 {
|
||||
address := textPage1Address
|
||||
if page == 1 {
|
||||
address = textPage2Address
|
||||
}
|
||||
|
||||
// See "Understand the Apple II", page 5-10
|
||||
// http://www.applelogic.org/files/UNDERSTANDINGTHEAII.pdf
|
||||
section := line / 8 // Top, middle and bottom
|
||||
eigth := line % 8
|
||||
address += uint16(section*40 + eigth*0x80 + col)
|
||||
return a.mmu.internalPeek(address)
|
||||
}
|
||||
|
||||
func snapshotTextMode(a *Apple2, page int) image.Image {
|
||||
width := textColumns * charWidth
|
||||
height := textLines * charHeight
|
||||
size := image.Rect(0, 0, width, height)
|
||||
bwPalette := []color.Color{color.Black, color.White}
|
||||
img := image.NewPaletted(size, bwPalette)
|
||||
|
||||
for x := 0; x < width; x++ {
|
||||
for y := 0; y < height; y++ {
|
||||
//yRev := height - y
|
||||
line := y / charHeight
|
||||
col := x / charWidth
|
||||
rowInChar := y % charHeight
|
||||
colInChar := x % charWidth
|
||||
char := getTextChar(a, col, line, page)
|
||||
pixel := a.cg.getPixel(char, rowInChar, colInChar)
|
||||
color := uint8(0)
|
||||
if pixel {
|
||||
color = 1
|
||||
}
|
||||
img.SetColorIndex(x, y, color)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return img
|
||||
}
|
|
@ -60,7 +60,7 @@ func softSwitchIntCxRomOff(io *ioC0Page) {
|
|||
}
|
||||
|
||||
func softSwitchSlotC3RomOn(io *ioC0Page) {
|
||||
if io.isSoftSwitchExtActive(ioFlagIntCxRom) {
|
||||
if io.isSoftSwitchActive(ioFlagIntCxRom) {
|
||||
return // Ignore if allt the Apple2 shadow ROM is active
|
||||
}
|
||||
// TODO restore the slot 3 ROM
|
||||
|
@ -69,7 +69,7 @@ func softSwitchSlotC3RomOn(io *ioC0Page) {
|
|||
}
|
||||
|
||||
func softSwitchSlotC3RomOff(io *ioC0Page) {
|
||||
if io.isSoftSwitchExtActive(ioFlagIntCxRom) {
|
||||
if io.isSoftSwitchActive(ioFlagIntCxRom) {
|
||||
return // Ignore if allt the Apple2 shadow ROM is active
|
||||
}
|
||||
mmu := io.apple2.mmu
|
||||
|
|
|
@ -10,6 +10,10 @@ func (p *unassignedPage) Peek(address uint8) uint8 {
|
|||
return 0xdd
|
||||
}
|
||||
|
||||
func (p *unassignedPage) internalPeek(address uint8) uint8 {
|
||||
return 0xdd
|
||||
}
|
||||
|
||||
func (p *unassignedPage) Poke(address uint8, value uint8) {
|
||||
//fmt.Printf("Write on address 0x%02x%02x\n", p.page, address)
|
||||
//panic(address)
|
||||
|
|
Loading…
Reference in New Issue
Block a user