mirror of
https://github.com/romychs/Ocean-240.2-Emulator.git
synced 2026-04-21 11:03:21 +03:00
Start FDC impl.
VScroll
This commit is contained in:
parent
62d732e45b
commit
231ae7a840
61
main.go
61
main.go
@ -12,6 +12,7 @@ import (
|
|||||||
"fyne.io/fyne/v2/app"
|
"fyne.io/fyne/v2/app"
|
||||||
"fyne.io/fyne/v2/canvas"
|
"fyne.io/fyne/v2/canvas"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/driver/desktop"
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -20,6 +21,8 @@ var BuildTime = "2026-03-01"
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
|
fmt.Printf("Starting Ocean-240.2 emulator %s build at %s\n", Version, BuildTime)
|
||||||
|
|
||||||
// base log init
|
// base log init
|
||||||
logger.InitLogging()
|
logger.InitLogging()
|
||||||
|
|
||||||
@ -30,12 +33,17 @@ func main() {
|
|||||||
|
|
||||||
// Reconfigure logging by config values
|
// Reconfigure logging by config values
|
||||||
//logger.ReconfigureLogging(conf)
|
//logger.ReconfigureLogging(conf)
|
||||||
|
|
||||||
emuapp := app.New()
|
|
||||||
w := emuapp.NewWindow("Океан 240.2")
|
|
||||||
|
|
||||||
computer := okean240.New(conf)
|
computer := okean240.New(conf)
|
||||||
|
|
||||||
|
emulatorApp := app.New()
|
||||||
|
w := emulatorApp.NewWindow("Океан 240.2")
|
||||||
|
w.Canvas().SetOnTypedKey(
|
||||||
|
func(key *fyne.KeyEvent) {
|
||||||
|
computer.PutKey(key)
|
||||||
|
})
|
||||||
|
|
||||||
|
addShortcuts(w.Canvas(), computer)
|
||||||
|
|
||||||
label := widget.NewLabel(fmt.Sprintf("Screen size: %dx%d", computer.ScreenWidth(), computer.ScreenHeight()))
|
label := widget.NewLabel(fmt.Sprintf("Screen size: %dx%d", computer.ScreenWidth(), computer.ScreenHeight()))
|
||||||
|
|
||||||
raster := canvas.NewRasterWithPixels(
|
raster := canvas.NewRasterWithPixels(
|
||||||
@ -44,35 +52,37 @@ func main() {
|
|||||||
})
|
})
|
||||||
raster.Resize(fyne.NewSize(512, 512))
|
raster.Resize(fyne.NewSize(512, 512))
|
||||||
raster.SetMinSize(fyne.NewSize(512, 512))
|
raster.SetMinSize(fyne.NewSize(512, 512))
|
||||||
|
|
||||||
|
centerRaster := container.NewCenter(raster)
|
||||||
|
|
||||||
w.Resize(fyne.NewSize(600, 600))
|
w.Resize(fyne.NewSize(600, 600))
|
||||||
|
|
||||||
hBox := container.NewHBox(
|
hBox := container.NewHBox(
|
||||||
|
widget.NewButton("Ctrl+C", func() {
|
||||||
|
computer.PutCtrlKey(0x03)
|
||||||
|
}),
|
||||||
|
widget.NewSeparator(),
|
||||||
widget.NewButton("Reset", func() {
|
widget.NewButton("Reset", func() {
|
||||||
computer.Reset()
|
computer.Reset()
|
||||||
}),
|
}),
|
||||||
|
widget.NewSeparator(),
|
||||||
widget.NewButton("Закрыть", func() {
|
widget.NewButton("Закрыть", func() {
|
||||||
emuapp.Quit()
|
emulatorApp.Quit()
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
vBox := container.NewVBox(
|
vBox := container.NewVBox(
|
||||||
raster,
|
centerRaster,
|
||||||
label,
|
label,
|
||||||
hBox,
|
hBox,
|
||||||
)
|
)
|
||||||
|
|
||||||
w.SetContent(vBox)
|
w.SetContent(vBox)
|
||||||
|
|
||||||
go emulator(computer, raster, label)
|
go emulator(computer, raster, label)
|
||||||
|
|
||||||
w.ShowAndRun()
|
w.ShowAndRun()
|
||||||
|
|
||||||
//println("Tick computer")
|
|
||||||
//computer := okean240.New(conf)
|
|
||||||
//println("Run computer")
|
|
||||||
//computer.Run()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const TicksPerFrame = 20_000_000 / 133
|
|
||||||
|
|
||||||
func emulator(computer *okean240.ComputerType, raster *canvas.Raster, label *widget.Label) {
|
func emulator(computer *okean240.ComputerType, raster *canvas.Raster, label *widget.Label) {
|
||||||
ticker := time.NewTicker(133 * time.Nanosecond)
|
ticker := time.NewTicker(133 * time.Nanosecond)
|
||||||
var ticks = 0
|
var ticks = 0
|
||||||
@ -81,6 +91,10 @@ func emulator(computer *okean240.ComputerType, raster *canvas.Raster, label *wid
|
|||||||
//var frameStartTime = time.Now().UnixMicro()
|
//var frameStartTime = time.Now().UnixMicro()
|
||||||
frameNextTime := time.Now().UnixMicro() + 20000
|
frameNextTime := time.Now().UnixMicro() + 20000
|
||||||
frame := 0
|
frame := 0
|
||||||
|
var pre uint64 = 0
|
||||||
|
var freq uint64 = 0
|
||||||
|
|
||||||
|
nextSecond := time.Now().Add(time.Second).UnixMicro()
|
||||||
curScrWidth := 256
|
curScrWidth := 256
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
ticks++
|
ticks++
|
||||||
@ -89,7 +103,13 @@ func emulator(computer *okean240.ComputerType, raster *canvas.Raster, label *wid
|
|||||||
computer.TimerClk()
|
computer.TimerClk()
|
||||||
}
|
}
|
||||||
if ticks > ticksCPU {
|
if ticks > ticksCPU {
|
||||||
ticksCPU = ticks + computer.Do()*3
|
ticksCPU = ticks + computer.Do()*2
|
||||||
|
}
|
||||||
|
|
||||||
|
if time.Now().UnixMicro() > nextSecond {
|
||||||
|
nextSecond = time.Now().Add(time.Second).UnixMicro()
|
||||||
|
freq = computer.Cycles() - pre
|
||||||
|
pre = computer.Cycles()
|
||||||
}
|
}
|
||||||
|
|
||||||
//if ticks >= ticksSCR {
|
//if ticks >= ticksSCR {
|
||||||
@ -107,11 +127,20 @@ func emulator(computer *okean240.ComputerType, raster *canvas.Raster, label *wid
|
|||||||
raster.Resize(newSize)
|
raster.Resize(newSize)
|
||||||
}
|
}
|
||||||
// status for every 25 frames
|
// status for every 25 frames
|
||||||
if frame%25 == 0 {
|
if frame%50 == 0 {
|
||||||
label.SetText(fmt.Sprintf("Screen size: %dx%d Tick: %d", computer.ScreenWidth(), computer.ScreenHeight(), computer.Cycles()))
|
label.SetText(fmt.Sprintf("Screen size: %dx%d F: %d", computer.ScreenWidth(), computer.ScreenHeight(), freq))
|
||||||
}
|
}
|
||||||
raster.Refresh()
|
raster.Refresh()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addShortcuts(c fyne.Canvas, computer *okean240.ComputerType) {
|
||||||
|
// Add shortcuts for Ctrl+A to Ctrl+Z
|
||||||
|
for kName := 'A'; kName <= 'Z'; kName++ {
|
||||||
|
kk := fyne.KeyName(kName)
|
||||||
|
sc := &desktop.CustomShortcut{KeyName: kk, Modifier: fyne.KeyModifierControl}
|
||||||
|
c.AddShortcut(sc, func(shortcut fyne.Shortcut) { computer.PutCtrlKey(byte(kName&0xff) - 0x40) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"okemu/config"
|
"okemu/config"
|
||||||
"okemu/z80em"
|
"okemu/z80em"
|
||||||
|
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,12 +23,17 @@ type ComputerType struct {
|
|||||||
bgColor byte
|
bgColor byte
|
||||||
dd70 *Timer8253
|
dd70 *Timer8253
|
||||||
dd72 *Sio8251
|
dd72 *Sio8251
|
||||||
|
fdc *FDCType
|
||||||
|
kbdBuffer []byte
|
||||||
|
vShift byte
|
||||||
|
hShift byte
|
||||||
}
|
}
|
||||||
|
|
||||||
const VRAMBlock0 = 3
|
const VRAMBlock0 = 3
|
||||||
const VRAMBlock1 = 7
|
const VRAMBlock1 = 7
|
||||||
const VidVsuBit = 0x80
|
const VidVsuBit = 0x80
|
||||||
const VidColorBit = 0x40
|
const VidColorBit = 0x40
|
||||||
|
const KbdBufferSize = 3
|
||||||
|
|
||||||
type ComputerInterface interface {
|
type ComputerInterface interface {
|
||||||
Run()
|
Run()
|
||||||
@ -35,6 +41,8 @@ type ComputerInterface interface {
|
|||||||
GetPixel(x uint16, y uint16) color.RGBA
|
GetPixel(x uint16, y uint16) color.RGBA
|
||||||
Do() uint64
|
Do() uint64
|
||||||
TimerClk()
|
TimerClk()
|
||||||
|
PutKey(key *fyne.KeyEvent)
|
||||||
|
PutCtrlKey(shortcut fyne.Shortcut)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ComputerType) M1MemRead(addr uint16) byte {
|
func (c *ComputerType) M1MemRead(addr uint16) byte {
|
||||||
@ -49,68 +57,6 @@ func (c *ComputerType) MemWrite(addr uint16, val byte) {
|
|||||||
c.memory.MemWrite(addr, val)
|
c.memory.MemWrite(addr, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ComputerType) IORead(port uint16) byte {
|
|
||||||
switch port & 0x00ff {
|
|
||||||
case PIC_DD75RS:
|
|
||||||
v := c.ioPorts[PIC_DD75RS]
|
|
||||||
c.ioPorts[PIC_DD75RS] = 0
|
|
||||||
return v
|
|
||||||
default:
|
|
||||||
log.Debugf("IORead from port: %x", port)
|
|
||||||
}
|
|
||||||
return c.ioPorts[byte(port&0x00ff)]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ComputerType) IOWrite(port uint16, val byte) {
|
|
||||||
bp := byte(port & 0x00ff)
|
|
||||||
c.ioPorts[bp] = val
|
|
||||||
//log.Debugf("OUT (%x), %x", bp, val)
|
|
||||||
switch bp {
|
|
||||||
case SYS_DD17PB:
|
|
||||||
if c.dd17EnableOut {
|
|
||||||
c.memory.Configure(val)
|
|
||||||
}
|
|
||||||
case SYS_DD17CTR:
|
|
||||||
c.dd17EnableOut = val == 0x80
|
|
||||||
case VID_DD67PB:
|
|
||||||
if val&VidVsuBit == 0 {
|
|
||||||
// video page 0
|
|
||||||
c.vRAM = c.memory.allMemory[VRAMBlock0]
|
|
||||||
} else {
|
|
||||||
// video page 1
|
|
||||||
c.vRAM = c.memory.allMemory[VRAMBlock1]
|
|
||||||
}
|
|
||||||
if val&VidColorBit != 0 {
|
|
||||||
c.colorMode = true
|
|
||||||
c.screenWidth = 256
|
|
||||||
} else {
|
|
||||||
c.colorMode = false
|
|
||||||
c.screenWidth = 512
|
|
||||||
}
|
|
||||||
c.palette = val & 0x07
|
|
||||||
c.bgColor = val & 0x38 >> 3
|
|
||||||
case DD67CTR:
|
|
||||||
|
|
||||||
case TMR_DD70CTR:
|
|
||||||
// Timer VI63 config register
|
|
||||||
c.dd70.Configure(val)
|
|
||||||
case TMR_DD70C1:
|
|
||||||
// Timer VI63 counter0 register
|
|
||||||
c.dd70.Load(0, val)
|
|
||||||
case TMR_DD70C2:
|
|
||||||
// Timer VI63 counter1 register
|
|
||||||
c.dd70.Load(1, val)
|
|
||||||
case TMR_DD70C3:
|
|
||||||
// Timer VI63 counter2 register
|
|
||||||
c.dd70.Load(2, val)
|
|
||||||
|
|
||||||
case KBD_DD78CTR:
|
|
||||||
default:
|
|
||||||
//log.Debugf("OUT to Unknown port (%x), %x", bp, val)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// New Builds new computer
|
// New Builds new computer
|
||||||
func New(cfg *config.OkEmuConfig) *ComputerType {
|
func New(cfg *config.OkEmuConfig) *ComputerType {
|
||||||
c := ComputerType{}
|
c := ComputerType{}
|
||||||
@ -127,8 +73,12 @@ func New(cfg *config.OkEmuConfig) *ComputerType {
|
|||||||
c.palette = 0
|
c.palette = 0
|
||||||
c.bgColor = 0
|
c.bgColor = 0
|
||||||
|
|
||||||
|
c.vShift = 0
|
||||||
|
c.hShift = 0
|
||||||
|
|
||||||
c.dd70 = NewTimer8253()
|
c.dd70 = NewTimer8253()
|
||||||
c.dd72 = NewSio8251()
|
c.dd72 = NewSio8251()
|
||||||
|
c.fdc = NewFDCType()
|
||||||
|
|
||||||
return &c
|
return &c
|
||||||
}
|
}
|
||||||
@ -136,6 +86,8 @@ func New(cfg *config.OkEmuConfig) *ComputerType {
|
|||||||
func (c *ComputerType) Reset() {
|
func (c *ComputerType) Reset() {
|
||||||
c.cpu.Reset()
|
c.cpu.Reset()
|
||||||
c.cycles = 0
|
c.cycles = 0
|
||||||
|
c.vShift = 0
|
||||||
|
c.hShift = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ComputerType) Do() int {
|
func (c *ComputerType) Do() int {
|
||||||
@ -160,18 +112,16 @@ func (c *ComputerType) GetPixel(x uint16, y uint16) color.RGBA {
|
|||||||
if x > 255 {
|
if x > 255 {
|
||||||
return CWhite
|
return CWhite
|
||||||
}
|
}
|
||||||
|
y += uint16(c.vShift)
|
||||||
|
// x += uint16(c.hShift >> 3)
|
||||||
// Color 256x256 mode
|
// Color 256x256 mode
|
||||||
addr = ((x & 0xf8) << 6) | y
|
addr = ((x & 0xf8) << 6) | (y & 0xff)
|
||||||
var mask byte = 1 << (x & 0x07)
|
if c.vShift != 0 {
|
||||||
pix1 := c.vRAM.memory[addr]&(mask) != 0
|
addr -= 8
|
||||||
pix2 := c.vRAM.memory[addr+0x100]&(mask) != 0
|
|
||||||
var cl byte = 0
|
|
||||||
if pix1 {
|
|
||||||
cl |= 1
|
|
||||||
}
|
|
||||||
if pix2 {
|
|
||||||
cl |= 2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var cl byte = (c.vRAM.memory[addr&0x3fff] >> (x & 0x07)) & 1
|
||||||
|
cl |= (c.vRAM.memory[(addr+0x100)&0x3fff] >> (x & 0x07)) & 1 << 1
|
||||||
if cl == 0 {
|
if cl == 0 {
|
||||||
resColor = BgColorPalette[c.bgColor]
|
resColor = BgColorPalette[c.bgColor]
|
||||||
} else {
|
} else {
|
||||||
@ -182,7 +132,8 @@ func (c *ComputerType) GetPixel(x uint16, y uint16) color.RGBA {
|
|||||||
return CWhite
|
return CWhite
|
||||||
}
|
}
|
||||||
// Mono 512x256 mode
|
// Mono 512x256 mode
|
||||||
addr = ((x & 0xf8) << 5) | y
|
y += uint16(c.vShift)
|
||||||
|
addr = ((x & 0xf8) << 5) | (y & 0xff)
|
||||||
pix := c.vRAM.memory[addr]&(1<<x) != 0
|
pix := c.vRAM.memory[addr]&(1<<x) != 0
|
||||||
if c.palette == 6 {
|
if c.palette == 6 {
|
||||||
if !pix {
|
if !pix {
|
||||||
|
|||||||
320
okean240/constants.go
Normal file
320
okean240/constants.go
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
package okean240
|
||||||
|
|
||||||
|
import "fyne.io/fyne/v2"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* КР580ВВ55 DD79 USER PORT
|
||||||
|
*/
|
||||||
|
|
||||||
|
// USR_DD79PA User port A
|
||||||
|
const USR_DD79PA = 0x00
|
||||||
|
|
||||||
|
// USR_DD79PB User port B
|
||||||
|
const USR_DD79PB = 0x01
|
||||||
|
|
||||||
|
// USR_DD79PC User port C
|
||||||
|
const USR_DD79PC = 0x02
|
||||||
|
|
||||||
|
// USR_DD79CTR Config
|
||||||
|
const USR_DD79CTR = 0x03 // Config: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI]
|
||||||
|
// Set bit: [0][xxx][bbb][0|1]
|
||||||
|
|
||||||
|
/*
|
||||||
|
* КР1818ВГ93 FDC Controller
|
||||||
|
*/
|
||||||
|
|
||||||
|
// FDC_CMD FDC Command
|
||||||
|
const FDC_CMD = 0x20
|
||||||
|
|
||||||
|
// FDC_TRACK FDC Track No
|
||||||
|
const FDC_TRACK = 0x21
|
||||||
|
|
||||||
|
// FDC_SECT FDC Sector
|
||||||
|
const FDC_SECT = 0x22
|
||||||
|
|
||||||
|
// FDC_DATA FDC Data
|
||||||
|
const FDC_DATA = 0x23
|
||||||
|
|
||||||
|
// FDC_WAIT FDC Wait
|
||||||
|
const FDC_WAIT = 0x24
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Floppy Controller port
|
||||||
|
*/
|
||||||
|
|
||||||
|
// FLOPPY Floppy Controller port
|
||||||
|
const FLOPPY = 0x25 // WR: 5-SSEN, 4-#DDEN, 3-INIT, 2-DRSEL, 1-MOT1, 0-MOT0
|
||||||
|
// RD: 7-MOTST, 6-SSEL, 5,4-x , 3-DRSEL, 2-MOT1, 1-MOT0, 0-INT
|
||||||
|
|
||||||
|
/*
|
||||||
|
* КР580ВВ55 DD78 Keyboard
|
||||||
|
*/
|
||||||
|
|
||||||
|
// KBD_DD78PA Port A - Keyboard Data
|
||||||
|
const KBD_DD78PA = 0x40
|
||||||
|
|
||||||
|
// KBD_DD78PB Port B - JST3,SHFT,CTRL,ACK,TAPE5,TAPE4,GK,GC
|
||||||
|
const KBD_DD78PB = 0x41
|
||||||
|
|
||||||
|
// KBD_DD78PC Port C - [PC7:5],[KBD_ACK],[PC3:0]
|
||||||
|
const KBD_DD78PC = 0x42
|
||||||
|
|
||||||
|
// KBD_DD78CTR Control port
|
||||||
|
const KBD_DD78CTR = 0x43 // Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI]
|
||||||
|
// Set bit: [0][xxx][bbb][0|1]
|
||||||
|
|
||||||
|
/*
|
||||||
|
* КР580ВИ53 DD70
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TMR_DD70C1 Timer load 1
|
||||||
|
const TMR_DD70C1 = 0x60
|
||||||
|
|
||||||
|
// TMR_DD70C2 Timer load 2
|
||||||
|
const TMR_DD70C2 = 0x61
|
||||||
|
|
||||||
|
// TMR_DD70C3 Timer load 3
|
||||||
|
const TMR_DD70C3 = 0x62
|
||||||
|
|
||||||
|
/*
|
||||||
|
TMR_DD70CTR
|
||||||
|
Timer config: [sc1,sc0][rl1,rl0][m2,m1,m0][bcd]
|
||||||
|
sc - timer, rl=01-LSB, 10-MSB, 11-LSB+MSB
|
||||||
|
mode 000 - intr on fin,
|
||||||
|
001 - one shot,
|
||||||
|
x10 - rate gen,
|
||||||
|
x11-sq wave
|
||||||
|
*/
|
||||||
|
const TMR_DD70CTR = 0x63
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Programmable Interrupt controller PIC KR580VV59
|
||||||
|
*/
|
||||||
|
|
||||||
|
// PIC_DD75RS RS Port
|
||||||
|
const PIC_DD75RS = 0x80
|
||||||
|
|
||||||
|
const Rst0SysFlag = 0x01 // System interrupt
|
||||||
|
const Rst1KbdFlag = 0x02 // Keyboard interrupt
|
||||||
|
const Rst2SerFlag = 0x04 // Serial interface interrupt
|
||||||
|
const RstЗLptFlag = 0x08 // Printer ready
|
||||||
|
const Rst4TmrFlag = 0x10 // System timer
|
||||||
|
const Rst5PwrFlag = 0x20 // Power intr
|
||||||
|
const Rst6UsrFlag = 0x40 // User device 1 interrupt
|
||||||
|
const Rst7UsrFlag = 0x80 // User device 1 interrupt
|
||||||
|
|
||||||
|
// PIC_DD75RM RM Port
|
||||||
|
const PIC_DD75RM = 0x81
|
||||||
|
|
||||||
|
/*
|
||||||
|
* КР580ВВ51 DD72
|
||||||
|
*/
|
||||||
|
|
||||||
|
// UART_DD72RD Serial data
|
||||||
|
const UART_DD72RD = 0xA0
|
||||||
|
|
||||||
|
// UART_DD72RR Serial status [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY]
|
||||||
|
const UART_DD72RR = 0xA1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* КР580ВВ55 DD17 System port
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Port A - VShift[8..1] Vertical shift
|
||||||
|
const SYS_DD17PA = 0xC0
|
||||||
|
|
||||||
|
// Port B - Memory mapper [ROM14,13][REST][ENROM-][A18,17,16][32k]
|
||||||
|
const SYS_DD17PB = 0xC1
|
||||||
|
|
||||||
|
// Port C - HShift[HS5..1,SB3..1] Horisontal shift
|
||||||
|
const SYS_DD17PC = 0xC2
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SYS_DD17CTR
|
||||||
|
* Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI]
|
||||||
|
* Set bit: [0][xxx][bbb][0|1]
|
||||||
|
*/
|
||||||
|
const SYS_DD17CTR = 0xC3
|
||||||
|
|
||||||
|
/*
|
||||||
|
* КР580ВВ55 DD67
|
||||||
|
*/
|
||||||
|
|
||||||
|
// LPT_DD67PA Port A - Printer Data
|
||||||
|
const LPT_DD67PA = 0xE0
|
||||||
|
|
||||||
|
// VID_DD67PB Port B - Video control [VSU,C/M,FL3:1,COL3:1]
|
||||||
|
const VID_DD67PB = 0xE1
|
||||||
|
|
||||||
|
// DD67PC Port C - [USER3:1, STB-LP, BELL, TAPE3:1]
|
||||||
|
const DD67PC = 0xE2
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DD67CTR
|
||||||
|
* Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI]
|
||||||
|
* Set bit: [0][xxx][bbb][0|1]
|
||||||
|
*/
|
||||||
|
const DD67CTR = 0xE3
|
||||||
|
|
||||||
|
var RemapKey = map[fyne.KeyName]byte{
|
||||||
|
fyne.KeyEscape: 0x1B,
|
||||||
|
fyne.KeyReturn: 0x0A,
|
||||||
|
fyne.KeyTab: 0x09,
|
||||||
|
fyne.KeyBackspace: 0x08,
|
||||||
|
fyne.KeyInsert: 0x00,
|
||||||
|
fyne.KeyDelete: 0x08,
|
||||||
|
fyne.KeyRight: 0x18,
|
||||||
|
fyne.KeyLeft: 0x08,
|
||||||
|
fyne.KeyDown: 0x0A,
|
||||||
|
fyne.KeyUp: 0x19,
|
||||||
|
fyne.KeyPageUp: 0x00,
|
||||||
|
fyne.KeyPageDown: 0x00,
|
||||||
|
fyne.KeyHome: 0x0C,
|
||||||
|
fyne.KeyEnd: 0x1A,
|
||||||
|
fyne.KeyF1: 0x00,
|
||||||
|
fyne.KeyF2: 0x00,
|
||||||
|
fyne.KeyF3: 0x00,
|
||||||
|
fyne.KeyF4: 0x00,
|
||||||
|
fyne.KeyF5: 0x00,
|
||||||
|
fyne.KeyF6: 0x00,
|
||||||
|
fyne.KeyF7: 0x00,
|
||||||
|
fyne.KeyF8: 0x00,
|
||||||
|
fyne.KeyF9: 0x00,
|
||||||
|
fyne.KeyF10: 0x00,
|
||||||
|
fyne.KeyF11: 0x00,
|
||||||
|
fyne.KeyF12: 0x00,
|
||||||
|
fyne.KeyEnter: 0x0D,
|
||||||
|
fyne.Key0: 0x30,
|
||||||
|
fyne.Key1: 0x31,
|
||||||
|
fyne.Key2: 0x32,
|
||||||
|
fyne.Key3: 0x33,
|
||||||
|
fyne.Key4: 0x34,
|
||||||
|
fyne.Key5: 0x35,
|
||||||
|
fyne.Key6: 0x36,
|
||||||
|
fyne.Key7: 0x37,
|
||||||
|
fyne.Key8: 0x38,
|
||||||
|
fyne.Key9: 0x39,
|
||||||
|
fyne.KeyA: 0x61,
|
||||||
|
fyne.KeyB: 0x62,
|
||||||
|
fyne.KeyC: 0x63,
|
||||||
|
fyne.KeyD: 0x64,
|
||||||
|
fyne.KeyE: 0x65,
|
||||||
|
fyne.KeyF: 0x66,
|
||||||
|
fyne.KeyG: 0x67,
|
||||||
|
fyne.KeyH: 0x68,
|
||||||
|
fyne.KeyI: 0x69,
|
||||||
|
fyne.KeyJ: 0x6a,
|
||||||
|
fyne.KeyK: 0x6b,
|
||||||
|
fyne.KeyL: 0x6c,
|
||||||
|
fyne.KeyM: 0x6d,
|
||||||
|
fyne.KeyN: 0x6e,
|
||||||
|
fyne.KeyO: 0x6f,
|
||||||
|
fyne.KeyP: 0x70,
|
||||||
|
fyne.KeyQ: 0x71,
|
||||||
|
fyne.KeyR: 0x72,
|
||||||
|
fyne.KeyS: 0x73,
|
||||||
|
fyne.KeyT: 0x74,
|
||||||
|
fyne.KeyU: 0x75,
|
||||||
|
fyne.KeyV: 0x76,
|
||||||
|
fyne.KeyW: 0x77,
|
||||||
|
fyne.KeyX: 0x78,
|
||||||
|
fyne.KeyY: 0x79,
|
||||||
|
fyne.KeyZ: 0x7A,
|
||||||
|
fyne.KeySpace: 0x20,
|
||||||
|
fyne.KeyApostrophe: 0x27,
|
||||||
|
fyne.KeyComma: 0x2c,
|
||||||
|
fyne.KeyMinus: 0x2d,
|
||||||
|
fyne.KeyPeriod: 0x2E,
|
||||||
|
fyne.KeySlash: 0x2F,
|
||||||
|
fyne.KeyBackslash: 0x5C,
|
||||||
|
fyne.KeyLeftBracket: 0x5B,
|
||||||
|
fyne.KeyRightBracket: 0x5D,
|
||||||
|
fyne.KeySemicolon: 0x3B,
|
||||||
|
fyne.KeyEqual: 0x3D,
|
||||||
|
fyne.KeyAsterisk: 0x2A,
|
||||||
|
fyne.KeyPlus: 0x2B,
|
||||||
|
fyne.KeyBackTick: 0x60,
|
||||||
|
fyne.KeyUnknown: 0x00,
|
||||||
|
}
|
||||||
|
|
||||||
|
var RemapKeyShift = map[fyne.KeyName]byte{
|
||||||
|
fyne.KeyEscape: 0x1B,
|
||||||
|
fyne.KeyReturn: 0x0A,
|
||||||
|
fyne.KeyTab: 0x09,
|
||||||
|
fyne.KeyBackspace: 0x08,
|
||||||
|
fyne.KeyInsert: 0x00,
|
||||||
|
fyne.KeyDelete: 0x08,
|
||||||
|
fyne.KeyRight: 0x18,
|
||||||
|
fyne.KeyLeft: 0x08,
|
||||||
|
fyne.KeyDown: 0x0A,
|
||||||
|
fyne.KeyUp: 0x19,
|
||||||
|
fyne.KeyPageUp: 0x00,
|
||||||
|
fyne.KeyPageDown: 0x00,
|
||||||
|
fyne.KeyHome: 0x0C,
|
||||||
|
fyne.KeyEnd: 0x1A,
|
||||||
|
fyne.KeyF1: 0x00,
|
||||||
|
fyne.KeyF2: 0x00,
|
||||||
|
fyne.KeyF3: 0x00,
|
||||||
|
fyne.KeyF4: 0x00,
|
||||||
|
fyne.KeyF5: 0x00,
|
||||||
|
fyne.KeyF6: 0x00,
|
||||||
|
fyne.KeyF7: 0x00,
|
||||||
|
fyne.KeyF8: 0x00,
|
||||||
|
fyne.KeyF9: 0x00,
|
||||||
|
fyne.KeyF10: 0x00,
|
||||||
|
fyne.KeyF11: 0x00,
|
||||||
|
fyne.KeyF12: 0x00,
|
||||||
|
fyne.KeyEnter: 0x0D,
|
||||||
|
|
||||||
|
fyne.Key0: 0x29,
|
||||||
|
fyne.Key1: 0x21,
|
||||||
|
fyne.Key2: 0x40,
|
||||||
|
fyne.Key3: 0x23,
|
||||||
|
fyne.Key4: 0x24,
|
||||||
|
fyne.Key5: 0x25,
|
||||||
|
fyne.Key6: 0x5E,
|
||||||
|
fyne.Key7: 0x26,
|
||||||
|
fyne.Key8: 0x2A,
|
||||||
|
fyne.Key9: 0x28,
|
||||||
|
fyne.KeyA: 0x41,
|
||||||
|
fyne.KeyB: 0x42,
|
||||||
|
fyne.KeyC: 0x43,
|
||||||
|
fyne.KeyD: 0x44,
|
||||||
|
fyne.KeyE: 0x45,
|
||||||
|
fyne.KeyF: 0x46,
|
||||||
|
fyne.KeyG: 0x47,
|
||||||
|
fyne.KeyH: 0x48,
|
||||||
|
fyne.KeyI: 0x49,
|
||||||
|
fyne.KeyJ: 0x4a,
|
||||||
|
fyne.KeyK: 0x4b,
|
||||||
|
fyne.KeyL: 0x4c,
|
||||||
|
fyne.KeyM: 0x4d,
|
||||||
|
fyne.KeyN: 0x4e,
|
||||||
|
fyne.KeyO: 0x4f,
|
||||||
|
fyne.KeyP: 0x50,
|
||||||
|
fyne.KeyQ: 0x51,
|
||||||
|
fyne.KeyR: 0x52,
|
||||||
|
fyne.KeyS: 0x53,
|
||||||
|
fyne.KeyT: 0x54,
|
||||||
|
fyne.KeyU: 0x55,
|
||||||
|
fyne.KeyV: 0x56,
|
||||||
|
fyne.KeyW: 0x57,
|
||||||
|
fyne.KeyX: 0x58,
|
||||||
|
fyne.KeyY: 0x59,
|
||||||
|
fyne.KeyZ: 0x5A,
|
||||||
|
fyne.KeySpace: 0x20,
|
||||||
|
fyne.KeyApostrophe: 0x22,
|
||||||
|
fyne.KeyComma: 0x3C,
|
||||||
|
fyne.KeyMinus: 0x5F,
|
||||||
|
fyne.KeyPeriod: 0x3E,
|
||||||
|
fyne.KeySlash: 0x3F,
|
||||||
|
fyne.KeyBackslash: 0x7C,
|
||||||
|
fyne.KeyLeftBracket: 0x7B,
|
||||||
|
fyne.KeyRightBracket: 0x7D,
|
||||||
|
fyne.KeySemicolon: 0x3A,
|
||||||
|
fyne.KeyEqual: 0x2B,
|
||||||
|
fyne.KeyAsterisk: 0x7E,
|
||||||
|
fyne.KeyPlus: 0x7E,
|
||||||
|
fyne.KeyBackTick: 0x60,
|
||||||
|
fyne.KeyUnknown: 0x00,
|
||||||
|
}
|
||||||
100
okean240/fdc.go
Normal file
100
okean240/fdc.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package okean240
|
||||||
|
|
||||||
|
import log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
const FloppySizeK = 360
|
||||||
|
const SectorSize = 128
|
||||||
|
const SectorPerTrack = 36
|
||||||
|
const FloppySizeInS = FloppySizeK * 1024 / SectorSize
|
||||||
|
const TracksCount = FloppySizeInS / SectorPerTrack
|
||||||
|
|
||||||
|
type SectorType []byte
|
||||||
|
|
||||||
|
type FDCType struct {
|
||||||
|
// Floppy controller port
|
||||||
|
ssen byte
|
||||||
|
deenN byte
|
||||||
|
init byte
|
||||||
|
drsel byte
|
||||||
|
mot1 byte
|
||||||
|
mot0 byte
|
||||||
|
intr byte
|
||||||
|
motst byte
|
||||||
|
sector byte
|
||||||
|
track byte
|
||||||
|
// FloppyStorage
|
||||||
|
sectors [FloppySizeInS]SectorType
|
||||||
|
data byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type FDCTypeInterface interface {
|
||||||
|
SetFloppy()
|
||||||
|
GetFloppy() byte
|
||||||
|
SetCmd(value byte)
|
||||||
|
SetTrack(value byte)
|
||||||
|
SetSector(value byte)
|
||||||
|
SetData(value byte)
|
||||||
|
Data() byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FDCType) SetFloppy(val byte) {
|
||||||
|
// WR: 5-SSEN, 4-#DDEN, 3-INIT, 2-DRSEL, 1-MOT1, 0-MOT0
|
||||||
|
f.ssen = val >> 5 & 0x01
|
||||||
|
f.deenN = val >> 4 & 0x01
|
||||||
|
f.init = val >> 3 & 0x01
|
||||||
|
f.drsel = val >> 2 & 0x01
|
||||||
|
f.mot1 = val >> 1 & 0x01
|
||||||
|
f.mot0 = val & 0x01
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FDCType) GetFloppy() byte {
|
||||||
|
// RD: 7-MOTST, 6-SSEL, 5,4-x , 3-DRSEL, 2-MOT1, 1-MOT0, 0-INT
|
||||||
|
floppy := f.intr | (f.mot0 << 1) | (f.mot1 << 2) | (f.drsel << 3) | (f.ssen << 6) | (f.motst << 7)
|
||||||
|
return floppy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FDCType) SetCmd(value byte) {
|
||||||
|
log.Debugf("FCD CMD: %x", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FDCType) SetTrack(value byte) {
|
||||||
|
log.Debugf("FCD Track: %d", value)
|
||||||
|
f.track = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FDCType) SetSector(value byte) {
|
||||||
|
log.Debugf("FCD Sector: %d", value)
|
||||||
|
f.sector = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FDCType) SetData(value byte) {
|
||||||
|
log.Debugf("FCD Data: %d", value)
|
||||||
|
f.data = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FDCType) GetData() byte {
|
||||||
|
return f.data
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFDCType() *FDCType {
|
||||||
|
sec := [FloppySizeInS]SectorType{}
|
||||||
|
for i := 0; i < FloppySizeInS; i++ {
|
||||||
|
sec[i] = make(SectorType, SectorSize)
|
||||||
|
for s := 0; s < 128; s++ {
|
||||||
|
sec[i][s] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &FDCType{
|
||||||
|
ssen: 0,
|
||||||
|
deenN: 0,
|
||||||
|
init: 0,
|
||||||
|
drsel: 0,
|
||||||
|
mot1: 0,
|
||||||
|
mot0: 0,
|
||||||
|
intr: 0,
|
||||||
|
motst: 0,
|
||||||
|
sectors: sec,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
@ -8,156 +8,96 @@ package okean240
|
|||||||
* By Romych 2026-03-01
|
* By Romych 2026-03-01
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
import log "github.com/sirupsen/logrus"
|
||||||
* КР580ВВ55 DD79 USER PORT
|
|
||||||
*/
|
|
||||||
|
|
||||||
// USR_DD79PA User port A
|
func (c *ComputerType) IORead(port uint16) byte {
|
||||||
const USR_DD79PA = 0x00
|
switch port & 0x00ff {
|
||||||
|
case PIC_DD75RS:
|
||||||
|
// PIO VN59
|
||||||
|
v := c.ioPorts[PIC_DD75RS]
|
||||||
|
c.ioPorts[PIC_DD75RS] = 0
|
||||||
|
return v
|
||||||
|
case UART_DD72RR:
|
||||||
|
// SIO VV51 CMD
|
||||||
|
return c.dd72.Status()
|
||||||
|
case UART_DD72RD:
|
||||||
|
// SIO VV51 Data
|
||||||
|
return c.dd72.Receive()
|
||||||
|
case KBD_DD78PA:
|
||||||
|
// Keyboard data
|
||||||
|
return c.ioPorts[KBD_DD78PA]
|
||||||
|
case KBD_DD78PB:
|
||||||
|
return c.ioPorts[KBD_DD78PB]
|
||||||
|
case FLOPPY:
|
||||||
|
return c.fdc.GetFloppy()
|
||||||
|
default:
|
||||||
|
log.Debugf("IORead from port: %x", port)
|
||||||
|
}
|
||||||
|
return c.ioPorts[byte(port&0x00ff)]
|
||||||
|
}
|
||||||
|
|
||||||
// USR_DD79PB User port B
|
func (c *ComputerType) IOWrite(port uint16, val byte) {
|
||||||
const USR_DD79PB = 0x01
|
bp := byte(port & 0x00ff)
|
||||||
|
c.ioPorts[bp] = val
|
||||||
|
//log.Debugf("OUT (%x), %x", bp, val)
|
||||||
|
switch bp {
|
||||||
|
case SYS_DD17PB:
|
||||||
|
if c.dd17EnableOut {
|
||||||
|
c.memory.Configure(val)
|
||||||
|
}
|
||||||
|
case SYS_DD17CTR:
|
||||||
|
c.dd17EnableOut = val == 0x80
|
||||||
|
case VID_DD67PB:
|
||||||
|
if val&VidVsuBit == 0 {
|
||||||
|
// video page 0
|
||||||
|
c.vRAM = c.memory.allMemory[VRAMBlock0]
|
||||||
|
} else {
|
||||||
|
// video page 1
|
||||||
|
c.vRAM = c.memory.allMemory[VRAMBlock1]
|
||||||
|
}
|
||||||
|
if val&VidColorBit != 0 {
|
||||||
|
c.colorMode = true
|
||||||
|
c.screenWidth = 256
|
||||||
|
} else {
|
||||||
|
c.colorMode = false
|
||||||
|
c.screenWidth = 512
|
||||||
|
}
|
||||||
|
c.palette = val & 0x07
|
||||||
|
c.bgColor = val & 0x38 >> 3
|
||||||
|
case SYS_DD17PA:
|
||||||
|
c.vShift = val
|
||||||
|
case SYS_DD17PC:
|
||||||
|
c.hShift = val
|
||||||
|
case TMR_DD70CTR:
|
||||||
|
// Timer VI63 config register
|
||||||
|
c.dd70.Configure(val)
|
||||||
|
case TMR_DD70C1:
|
||||||
|
// Timer VI63 counter0 register
|
||||||
|
c.dd70.Load(0, val)
|
||||||
|
case TMR_DD70C2:
|
||||||
|
// Timer VI63 counter1 register
|
||||||
|
c.dd70.Load(1, val)
|
||||||
|
case TMR_DD70C3:
|
||||||
|
// Timer VI63 counter2 register
|
||||||
|
c.dd70.Load(2, val)
|
||||||
|
|
||||||
// USR_DD79PC User port C
|
case UART_DD72RR:
|
||||||
const USR_DD79PC = 0x02
|
// SIO VV51 CMD
|
||||||
|
c.dd72.Command(val)
|
||||||
|
case UART_DD72RD:
|
||||||
|
// SIO VV51 Data
|
||||||
|
c.dd72.Send(val)
|
||||||
|
case FDC_CMD:
|
||||||
|
c.fdc.SetCmd(val)
|
||||||
|
case FDC_TRACK:
|
||||||
|
c.fdc.SetTrack(val)
|
||||||
|
case FDC_SECT:
|
||||||
|
c.fdc.SetSector(val)
|
||||||
|
case FLOPPY:
|
||||||
|
c.fdc.SetFloppy(val)
|
||||||
|
|
||||||
// USR_DD79CTR Config
|
default:
|
||||||
const USR_DD79CTR = 0x03 // Config: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI]
|
//log.Debugf("OUT to Unknown port (%x), %x", bp, val)
|
||||||
// Set bit: [0][xxx][bbb][0|1]
|
|
||||||
|
|
||||||
/*
|
}
|
||||||
* КР1818ВГ93 FDC Controller
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
// FDC_CMD FDC Command
|
|
||||||
const FDC_CMD = 0x20
|
|
||||||
|
|
||||||
// FDC_TRACK FDC Track No
|
|
||||||
const FDC_TRACK = 0x21
|
|
||||||
|
|
||||||
// FDC_SECT FDC Sector
|
|
||||||
const FDC_SECT = 0x22
|
|
||||||
|
|
||||||
// FDC_DATA FDC Data
|
|
||||||
const FDC_DATA = 0x23
|
|
||||||
|
|
||||||
// FDC_WAIT FDC Wait
|
|
||||||
const FDC_WAIT = 0x24
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Floppy Controller port
|
|
||||||
*/
|
|
||||||
|
|
||||||
// FLOPPY Floppy Controller port
|
|
||||||
const FLOPPY = 0x25 // WR: 5-SSEN, 4-#DDEN, 3-INIT, 2-DRSEL, 1-MOT1, 0-MOT0
|
|
||||||
// RD: 7-MOTST, 6-SSEL, 5,4-x , 3-DRSEL, 2-MOT1, 1-MOT0, 0-INT
|
|
||||||
|
|
||||||
/*
|
|
||||||
* КР580ВВ55 DD78 Keyboard
|
|
||||||
*/
|
|
||||||
|
|
||||||
// KBD_DD78PA Port A - Keyboard Data
|
|
||||||
const KBD_DD78PA = 0x40
|
|
||||||
|
|
||||||
// KBD_DD78PB Port B - JST3,SHFT,CTRL,ACK,TAPE5,TAPE4,GK,GC
|
|
||||||
const KBD_DD78PB = 0x41
|
|
||||||
|
|
||||||
// KBD_DD78PC Port C - [PC7:5],[KBD_ACK],[PC3:0]
|
|
||||||
const KBD_DD78PC = 0x42
|
|
||||||
|
|
||||||
// KBD_DD78CTR Control port
|
|
||||||
const KBD_DD78CTR = 0x43 // Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI]
|
|
||||||
// Set bit: [0][xxx][bbb][0|1]
|
|
||||||
|
|
||||||
/*
|
|
||||||
* КР580ВИ53 DD70
|
|
||||||
*/
|
|
||||||
|
|
||||||
// TMR_DD70C1 Timer load 1
|
|
||||||
const TMR_DD70C1 = 0x60
|
|
||||||
|
|
||||||
// TMR_DD70C2 Timer load 2
|
|
||||||
const TMR_DD70C2 = 0x61
|
|
||||||
|
|
||||||
// TMR_DD70C3 Timer load 3
|
|
||||||
const TMR_DD70C3 = 0x62
|
|
||||||
|
|
||||||
/*
|
|
||||||
TMR_DD70CTR
|
|
||||||
Timer config: [sc1,sc0][rl1,rl0][m2,m1,m0][bcd]
|
|
||||||
sc - timer, rl=01-LSB, 10-MSB, 11-LSB+MSB
|
|
||||||
mode 000 - int on fin,
|
|
||||||
001 - one shot,
|
|
||||||
x10 - rate gen,
|
|
||||||
x11-sq wave
|
|
||||||
*/
|
|
||||||
const TMR_DD70CTR = 0x63
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Programmable Interrupt controller PIC KR580VV59
|
|
||||||
*/
|
|
||||||
|
|
||||||
// PIC_DD75RS RS Port
|
|
||||||
const PIC_DD75RS = 0x80
|
|
||||||
|
|
||||||
const Rst0SysFlag = 0x01 // System interrupt
|
|
||||||
const Rst1KbdFlag = 0x02 // Keyboard interrupt
|
|
||||||
const Rst2SerFlag = 0x04 // Serial interface interrupt
|
|
||||||
const RstЗLptFlag = 0x08 // Printer ready
|
|
||||||
const Rst4TmrFlag = 0x10 // System timer
|
|
||||||
const Rst5PwrFlag = 0x20 // Power int
|
|
||||||
const Rst6UsrFlag = 0x40 // User device 1 interrupt
|
|
||||||
const Rst7UsrFlag = 0x80 // User device 1 interrupt
|
|
||||||
|
|
||||||
// PIC_DD75RM RM Port
|
|
||||||
const PIC_DD75RM = 0x81
|
|
||||||
|
|
||||||
/*
|
|
||||||
* КР580ВВ51 DD72
|
|
||||||
*/
|
|
||||||
|
|
||||||
// UART_DD72RD Serial data
|
|
||||||
const UART_DD72RD = 0xA0
|
|
||||||
|
|
||||||
// UART_DD72RR Serial status [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY]
|
|
||||||
const UART_DD72RR = 0xA1
|
|
||||||
|
|
||||||
/*
|
|
||||||
* КР580ВВ55 DD17 System port
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Port A - VShift[8..1] Vertical shift
|
|
||||||
const SYS_DD17PA = 0xC0
|
|
||||||
|
|
||||||
// Port B - Memory mapper [ROM14,13][REST][ENROM-][A18,17,16][32k]
|
|
||||||
const SYS_DD17PB = 0xC1
|
|
||||||
|
|
||||||
// Port C - HShift[HS5..1,SB3..1] Horisontal shift
|
|
||||||
const SYS_DD17PC = 0xC2
|
|
||||||
|
|
||||||
/*
|
|
||||||
* SYS_DD17CTR
|
|
||||||
* Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI]
|
|
||||||
* Set bit: [0][xxx][bbb][0|1]
|
|
||||||
*/
|
|
||||||
const SYS_DD17CTR = 0xC3
|
|
||||||
|
|
||||||
/*
|
|
||||||
* КР580ВВ55 DD67
|
|
||||||
*/
|
|
||||||
|
|
||||||
// LPT_DD67PA Port A - Printer Data
|
|
||||||
const LPT_DD67PA = 0xE0
|
|
||||||
|
|
||||||
// VID_DD67PB Port B - Video control [VSU,C/M,FL3:1,COL3:1]
|
|
||||||
const VID_DD67PB = 0xE1
|
|
||||||
|
|
||||||
// DD67PC Port C - [USER3:1, STB-LP, BELL, TAPE3:1]
|
|
||||||
const DD67PC = 0xE2
|
|
||||||
|
|
||||||
/*
|
|
||||||
* DD67CTR
|
|
||||||
* Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI]
|
|
||||||
* Set bit: [0][xxx][bbb][0|1]
|
|
||||||
*/
|
|
||||||
const DD67CTR = 0xE3
|
|
||||||
|
|||||||
63
okean240/keyboard.go
Normal file
63
okean240/keyboard.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package okean240
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *ComputerType) PutKey(key *fyne.KeyEvent) {
|
||||||
|
|
||||||
|
if key.Name == fyne.KeyUnknown {
|
||||||
|
log.Debugf("Unknown key scancode: %X", key.Physical.ScanCode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.kbdBuffer) < KbdBufferSize {
|
||||||
|
|
||||||
|
var code byte
|
||||||
|
|
||||||
|
if (c.ioPorts[KBD_DD78PB] & 0x40) == 0 {
|
||||||
|
// No shift
|
||||||
|
code = RemapKey[key.Name]
|
||||||
|
} else {
|
||||||
|
// Shift
|
||||||
|
code = RemapKeyShift[key.Name]
|
||||||
|
}
|
||||||
|
c.ioPorts[KBD_DD78PB] &= 0x1f
|
||||||
|
if code != 0 {
|
||||||
|
c.ioPorts[KBD_DD78PA] = code
|
||||||
|
c.ioPorts[PIC_DD75RS] |= Rst1KbdFlag
|
||||||
|
} else {
|
||||||
|
switch key.Name {
|
||||||
|
case "LeftAlt", "RightAlt":
|
||||||
|
c.ioPorts[KBD_DD78PB] |= 0x80
|
||||||
|
case "LeftControl", "RightControl":
|
||||||
|
c.ioPorts[KBD_DD78PB] |= 0x20
|
||||||
|
case "LeftShift", "RightShift":
|
||||||
|
c.ioPorts[KBD_DD78PB] |= 0x40
|
||||||
|
default:
|
||||||
|
log.Debugf("Unhandled KeyName: %s code: %X", key.Name, key.Physical.ScanCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
CTRL_C EQU 0x03 ; Warm boot
|
||||||
|
CTRL_H EQU 0x08 ; Backspace
|
||||||
|
CTRL_E EQU 0x05 ; Move to beginning of new line (Physical EOL)
|
||||||
|
CTRL_J EQU 0x0A ; LF - Line Feed
|
||||||
|
CTRL_M EQU 0x0D ; CR - Carriage Return
|
||||||
|
CTRL_P EQU 0x10 ; turn on/off printer
|
||||||
|
CTRL_R EQU 0x12 ; Repeat current cmd line
|
||||||
|
CTRL_S EQU 0x13 ; Temporary stop display data to console (aka DC3)
|
||||||
|
CTRL_U EQU 0x15 ; Cancel (erase) current cmd line
|
||||||
|
CTRL_X EQU 0x18 ; Cancel (erase) current cmd line
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (c *ComputerType) PutCtrlKey(key byte) {
|
||||||
|
c.ioPorts[KBD_DD78PA] = key
|
||||||
|
c.ioPorts[PIC_DD75RS] |= Rst1KbdFlag
|
||||||
|
c.ioPorts[KBD_DD78PB] &= 0x1f | 0x20
|
||||||
|
}
|
||||||
@ -38,7 +38,7 @@ type Memory struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MemoryInterface interface {
|
type MemoryInterface interface {
|
||||||
// Init - Initialize memory at "computer start"
|
// Init - Initialize memory at "computer started"
|
||||||
Init(rom0 string, rom1 string)
|
Init(rom0 string, rom1 string)
|
||||||
// Configure - Set memory configuration
|
// Configure - Set memory configuration
|
||||||
Configure(value byte)
|
Configure(value byte)
|
||||||
@ -64,7 +64,7 @@ func (m *Memory) Init(monFile string, cmpFile string) {
|
|||||||
m.allMemory[block] = &rb
|
m.allMemory[block] = &rb
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load ROM files and init ROM0,1
|
// Command ROM files and init ROM0,1
|
||||||
// Read the entire file into a byte slice
|
// Read the entire file into a byte slice
|
||||||
rom0bin, err := os.ReadFile(monFile)
|
rom0bin, err := os.ReadFile(monFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -1,19 +1,137 @@
|
|||||||
package okean240
|
package okean240
|
||||||
|
|
||||||
|
const I8251DSRFlag = 0x80
|
||||||
|
const I8251SynDetFlag = 0x40
|
||||||
|
const I8251FrameErrorFlag = 0x20
|
||||||
|
const I8251OverrunErrorFlag = 0x10
|
||||||
|
const I8251ParityErrorFlag = 0x08
|
||||||
|
const I8251TxEnableFlag = 0x04
|
||||||
|
const I8251RxReadyFlag = 0x02
|
||||||
|
const I8251TxReadyFlag = 0x01
|
||||||
|
const I8251TxBuffMaxLen = 16
|
||||||
|
|
||||||
|
const (
|
||||||
|
Sio8251Reset = iota
|
||||||
|
Sio8251LoadSyncChar1
|
||||||
|
Sio8251LoadSyncChar2
|
||||||
|
Sio8251LoadCommand
|
||||||
|
)
|
||||||
|
|
||||||
type Sio8251 struct {
|
type Sio8251 struct {
|
||||||
counter uint64
|
counter uint64
|
||||||
|
mode byte
|
||||||
|
initState byte
|
||||||
|
syncChar1 byte
|
||||||
|
syncChar2 byte
|
||||||
|
bufferRx []byte
|
||||||
|
bufferTx []byte
|
||||||
|
rxe bool
|
||||||
|
txe bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Sio8251Interface interface {
|
type Sio8251Interface interface {
|
||||||
Tick()
|
Tick()
|
||||||
|
Status() byte
|
||||||
|
Reset()
|
||||||
|
Command(value byte)
|
||||||
|
Send(value byte)
|
||||||
|
Receive() byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSio8251() *Sio8251 {
|
func NewSio8251() *Sio8251 {
|
||||||
return &Sio8251{
|
return &Sio8251{
|
||||||
counter: 0,
|
counter: 0,
|
||||||
|
mode: 0,
|
||||||
|
initState: 0,
|
||||||
|
rxe: false,
|
||||||
|
txe: false,
|
||||||
|
bufferRx: []byte{},
|
||||||
|
bufferTx: []byte{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sio8251) Tick() {
|
func (s *Sio8251) Tick() {
|
||||||
s.counter++
|
s.counter++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Status i8251 status [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY]
|
||||||
|
func (s *Sio8251) Status() byte {
|
||||||
|
var status byte = 0
|
||||||
|
if len(s.bufferRx) > 0 {
|
||||||
|
status |= I8251RxReadyFlag
|
||||||
|
}
|
||||||
|
if len(s.bufferTx) < I8251TxBuffMaxLen {
|
||||||
|
status |= I8251TxReadyFlag
|
||||||
|
}
|
||||||
|
if s.txe {
|
||||||
|
status |= I8251TxEnableFlag
|
||||||
|
}
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sio8251) Reset() {
|
||||||
|
s.counter = 0
|
||||||
|
s.mode = 0
|
||||||
|
s.initState = 0
|
||||||
|
s.bufferRx = make([]byte, 8)
|
||||||
|
s.bufferTx = make([]byte, I8251TxBuffMaxLen)
|
||||||
|
s.rxe = false
|
||||||
|
s.txe = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sio8251) Command(value byte) {
|
||||||
|
switch s.initState {
|
||||||
|
case Sio8251Reset:
|
||||||
|
s.mode = value
|
||||||
|
if s.mode&0x03 > 0 {
|
||||||
|
// SYNC
|
||||||
|
s.initState = Sio8251LoadSyncChar1
|
||||||
|
}
|
||||||
|
// ASYNC
|
||||||
|
s.initState = Sio8251LoadCommand
|
||||||
|
case Sio8251LoadSyncChar1:
|
||||||
|
s.mode = value
|
||||||
|
if s.mode&0x80 == 0 { // SYNC DOUBLE
|
||||||
|
s.initState = Sio8251LoadSyncChar2
|
||||||
|
}
|
||||||
|
case Sio8251LoadSyncChar2:
|
||||||
|
s.mode = value
|
||||||
|
s.initState = Sio8251LoadCommand
|
||||||
|
case Sio8251LoadCommand:
|
||||||
|
// value = command
|
||||||
|
if value&0x40 != 0 {
|
||||||
|
// RESET CMD
|
||||||
|
s.Reset()
|
||||||
|
} else {
|
||||||
|
// Set RXE, TXE
|
||||||
|
if value&0x04 != 0 {
|
||||||
|
s.rxe = true
|
||||||
|
} else {
|
||||||
|
s.rxe = false
|
||||||
|
}
|
||||||
|
if value&0x01 != 0 {
|
||||||
|
s.txe = true
|
||||||
|
} else {
|
||||||
|
s.txe = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sio8251) Send(value byte) {
|
||||||
|
if s.txe {
|
||||||
|
s.bufferTx = append(s.bufferTx, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sio8251) Receive() byte {
|
||||||
|
if s.rxe {
|
||||||
|
if len(s.bufferRx) > 0 {
|
||||||
|
res := s.bufferRx[0]
|
||||||
|
s.bufferRx = s.bufferRx[1:]
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|||||||
@ -1,14 +1,16 @@
|
|||||||
package okean240
|
package okean240
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Timer config: [sc1,sc0][rl1,rl0][m2,m1,m0][bcd]
|
Timer config byte: [sc1:0][rl1:0][m2:0][bcd]
|
||||||
sc - timer, rl=01-LSB, 10-MSB, 11-LSB+MSB
|
sc1:0 - timer No
|
||||||
mode 000 - int on fin,
|
rl=01-LSB, 10-MSB, 11-LSB+MSB
|
||||||
|
mode 000 - intr on fin,
|
||||||
001 - one shot,
|
001 - one shot,
|
||||||
x10 - rate gen,
|
x10 - rate gen,
|
||||||
x11 - sq wave
|
x11 - sq wave
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Timer work modes
|
||||||
const (
|
const (
|
||||||
TimerModeIntOnFin = iota
|
TimerModeIntOnFin = iota
|
||||||
TimerModeOneShot
|
TimerModeOneShot
|
||||||
@ -16,21 +18,22 @@ const (
|
|||||||
TimerModeSqWave
|
TimerModeSqWave
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Timer load counter modes
|
||||||
const (
|
const (
|
||||||
TimerRLMsbLsb = iota
|
TimerRLMsbLsb = iota
|
||||||
TimerRLLsbLsb
|
TimerRLLsb
|
||||||
TimerRLMsb
|
TimerRLMsb
|
||||||
TimerRLLsbMsb
|
TimerRLLsbMsb
|
||||||
)
|
)
|
||||||
|
|
||||||
type Timer8253Ch struct {
|
type Timer8253Ch struct {
|
||||||
rl byte
|
rl byte // load mode
|
||||||
mode byte
|
mode byte // counter mode
|
||||||
bcd bool
|
bcd bool // decimal/BCD load mode
|
||||||
load uint16
|
load uint16 // value to count from
|
||||||
counter uint16
|
counter uint16 // timer counter
|
||||||
fb bool
|
fb bool // first byte load flag
|
||||||
start bool
|
started bool // true if timer started
|
||||||
fired bool
|
fired bool
|
||||||
}
|
}
|
||||||
type Timer8253 struct {
|
type Timer8253 struct {
|
||||||
@ -60,22 +63,22 @@ func NewTimer8253() *Timer8253 {
|
|||||||
|
|
||||||
func (t *Timer8253) Tick(chNo int) {
|
func (t *Timer8253) Tick(chNo int) {
|
||||||
tmr := &t.channel[chNo]
|
tmr := &t.channel[chNo]
|
||||||
if tmr.start {
|
if tmr.started {
|
||||||
tmr.counter--
|
tmr.counter--
|
||||||
if tmr.counter == 0 {
|
if tmr.counter == 0 {
|
||||||
switch tmr.mode {
|
switch tmr.mode {
|
||||||
case TimerModeIntOnFin:
|
case TimerModeIntOnFin:
|
||||||
{
|
{
|
||||||
tmr.start = false
|
tmr.started = false
|
||||||
tmr.fired = true
|
tmr.fired = true
|
||||||
}
|
}
|
||||||
case TimerModeOneShot:
|
case TimerModeOneShot:
|
||||||
tmr.start = false
|
tmr.started = false
|
||||||
case TimerModeRateGen:
|
case TimerModeRateGen:
|
||||||
tmr.start = false
|
tmr.started = false
|
||||||
case TimerModeSqWave:
|
case TimerModeSqWave:
|
||||||
{
|
{
|
||||||
tmr.start = true
|
tmr.started = true
|
||||||
tmr.counter = tmr.load
|
tmr.counter = tmr.load
|
||||||
tmr.fired = true
|
tmr.fired = true
|
||||||
}
|
}
|
||||||
@ -97,13 +100,13 @@ func (t *Timer8253) Fired(chNo int) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Timer8253) Start(chNo int) bool {
|
func (t *Timer8253) Start(chNo int) bool {
|
||||||
return t.channel[chNo].start
|
return t.channel[chNo].started
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Timer8253) Configure(value byte) {
|
func (t *Timer8253) Configure(value byte) {
|
||||||
chNo := (value & 0xC0) >> 6
|
chNo := (value & 0xC0) >> 6
|
||||||
rl := value & 0x30 >> 4
|
rl := value & 0x30 >> 4
|
||||||
t.channel[chNo].start = false
|
t.channel[chNo].started = false
|
||||||
t.channel[chNo].rl = rl
|
t.channel[chNo].rl = rl
|
||||||
t.channel[chNo].mode = (value & 0x0E) >> 1
|
t.channel[chNo].mode = (value & 0x0E) >> 1
|
||||||
t.channel[chNo].fb = true
|
t.channel[chNo].fb = true
|
||||||
@ -113,7 +116,8 @@ func (t *Timer8253) Configure(value byte) {
|
|||||||
|
|
||||||
func (t *Timer8253) Load(chNo byte, value byte) {
|
func (t *Timer8253) Load(chNo byte, value byte) {
|
||||||
timer := &t.channel[chNo]
|
timer := &t.channel[chNo]
|
||||||
if timer.rl == 0 {
|
switch timer.rl {
|
||||||
|
case TimerRLMsbLsb:
|
||||||
// MSB+LSB
|
// MSB+LSB
|
||||||
if timer.fb {
|
if timer.fb {
|
||||||
// MSB
|
// MSB
|
||||||
@ -122,17 +126,17 @@ func (t *Timer8253) Load(chNo byte, value byte) {
|
|||||||
} else {
|
} else {
|
||||||
// LSB
|
// LSB
|
||||||
timer.load |= uint16(value)
|
timer.load |= uint16(value)
|
||||||
timer.start = true
|
timer.started = true
|
||||||
}
|
}
|
||||||
} else if timer.rl == 1 {
|
case TimerRLLsb:
|
||||||
// LSB Only
|
// LSB Only
|
||||||
timer.load = (timer.load & 0xff00) | uint16(value)
|
timer.load = (timer.load & 0xff00) | uint16(value)
|
||||||
timer.start = true
|
timer.started = true
|
||||||
} else if timer.rl == 2 {
|
case TimerRLMsb:
|
||||||
// MSB Only
|
// MSB Only
|
||||||
timer.load = (timer.load & 0x00ff) | (uint16(value) << 8)
|
timer.load = (timer.load & 0x00ff) | (uint16(value) << 8)
|
||||||
timer.start = true
|
timer.started = true
|
||||||
} else {
|
case TimerRLLsbMsb:
|
||||||
// LSB+MSB
|
// LSB+MSB
|
||||||
if timer.fb {
|
if timer.fb {
|
||||||
// LSB
|
// LSB
|
||||||
@ -141,7 +145,7 @@ func (t *Timer8253) Load(chNo byte, value byte) {
|
|||||||
} else {
|
} else {
|
||||||
// MSB
|
// MSB
|
||||||
timer.load = (uint16(value) << 8) | (timer.load & 0x00ff)
|
timer.load = (uint16(value) << 8) | (timer.load & 0x00ff)
|
||||||
timer.start = true
|
timer.started = true
|
||||||
timer.counter = timer.load
|
timer.counter = timer.load
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
logFile: "okemu.log"
|
logFile: "okemu.log"
|
||||||
logLevel: "info"
|
logLevel: "info"
|
||||||
monitorFile: "rom/MON_v5.bin"
|
monitorFile: "rom/MON_r6.bin"
|
||||||
cpmFile: "rom/CPM_v5.bin"
|
cpmFile: "rom/CPM_r7.bin"
|
||||||
|
|||||||
BIN
rom/CPM_r5.bin
Normal file
BIN
rom/CPM_r5.bin
Normal file
Binary file not shown.
BIN
rom/MON_r5.bin
Normal file
BIN
rom/MON_r5.bin
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user