├── GEMUSingle ├── Makefile ├── bindata.go └── main.go ├── Makefile ├── Readme.md ├── doc.go ├── hardware.go ├── hardware_clock.go ├── hardware_dcpu.go ├── hardware_eeprom.go ├── hardware_floppy.go ├── hardware_keyboard.go ├── hardware_lem.go ├── hardware_pixie.go ├── hardware_rom.go ├── storage.go ├── storage_disk.go ├── storage_flip.go └── storage_multi.go /GEMUSingle/Makefile: -------------------------------------------------------------------------------- 1 | BINARY=GEMUSingle 2 | 3 | .DEFAULT_GOAL: $(BINARY) 4 | .PHONY: ${BINARY} 5 | 6 | $(BINARY): 7 | go build -o ${BINARY} 8 | 9 | .PHONY: install 10 | install: 11 | go install ./... 12 | 13 | .PHONY: clean 14 | clean: 15 | if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi 16 | -------------------------------------------------------------------------------- /GEMUSingle/bindata.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-bindata. 2 | // sources: 3 | // bbos.bin 4 | // DO NOT EDIT! 5 | 6 | package main 7 | 8 | import ( 9 | "bytes" 10 | "compress/gzip" 11 | "fmt" 12 | "io" 13 | "io/ioutil" 14 | "os" 15 | "path/filepath" 16 | "strings" 17 | "time" 18 | ) 19 | 20 | func bindataRead(data []byte, name string) ([]byte, error) { 21 | gz, err := gzip.NewReader(bytes.NewBuffer(data)) 22 | if err != nil { 23 | return nil, fmt.Errorf("Read %q: %v", name, err) 24 | } 25 | 26 | var buf bytes.Buffer 27 | _, err = io.Copy(&buf, gz) 28 | clErr := gz.Close() 29 | 30 | if err != nil { 31 | return nil, fmt.Errorf("Read %q: %v", name, err) 32 | } 33 | if clErr != nil { 34 | return nil, err 35 | } 36 | 37 | return buf.Bytes(), nil 38 | } 39 | 40 | type asset struct { 41 | bytes []byte 42 | info os.FileInfo 43 | } 44 | 45 | type bindataFileInfo struct { 46 | name string 47 | size int64 48 | mode os.FileMode 49 | modTime time.Time 50 | } 51 | 52 | func (fi bindataFileInfo) Name() string { 53 | return fi.name 54 | } 55 | func (fi bindataFileInfo) Size() int64 { 56 | return fi.size 57 | } 58 | func (fi bindataFileInfo) Mode() os.FileMode { 59 | return fi.mode 60 | } 61 | func (fi bindataFileInfo) ModTime() time.Time { 62 | return fi.modTime 63 | } 64 | func (fi bindataFileInfo) IsDir() bool { 65 | return false 66 | } 67 | func (fi bindataFileInfo) Sys() interface{} { 68 | return nil 69 | } 70 | 71 | var _bbosBin = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x8c\x54\x51\x6c\x14\x55\x17\xfe\xce\x6e\xff\x7f\xf7\x6f\xf9\xff\x92\xf6\xb7\x25\xb4\x91\xbb\x4b\x53\x35\xc6\x88\x0d\x21\xc1\x48\x9c\x6d\x6b\x1a\x24\x08\x94\x21\x98\x18\x93\xd9\x1d\xd6\x50\xad\xdd\x64\x5b\xcc\x42\x2e\x7b\xf7\xc6\x29\xbb\x76\x6b\x68\x90\x44\x1f\x30\x38\x40\xc4\x68\x62\x22\x0f\x7d\x30\x86\x64\x8a\xc6\xa0\x89\x9a\x8a\x7d\xe0\x41\xb2\x3e\xf0\xa0\x91\xcc\xdc\x55\x5c\x78\x60\xcd\xdd\xa5\x2c\xbc\x39\x0f\xe7\xce\x39\xf7\xfb\xce\xb9\xe7\x9c\x7b\x2e\xf7\xb0\x86\x57\x7c\x70\x0a\xef\x2e\x85\xb7\xde\x71\xba\x4f\x4b\x21\x7d\x14\x93\x47\x0d\x7f\x3f\x67\xc1\xef\x9c\xa9\x1e\xce\x82\xeb\x9c\x05\xd7\x8a\xcb\xd5\x76\x21\xfd\x31\x27\xc6\x89\x85\x8b\x54\xa4\x08\x1d\xc5\xd8\x88\x45\xc7\x53\xa5\x6e\x21\xfd\x98\x58\xde\xf7\x31\xd5\x85\xf4\xf7\x94\xe2\xb9\x2d\x0d\xf4\x5a\x41\xd5\x01\x4e\x6b\xdb\xca\x0d\xec\xf1\x14\xa7\x4d\x28\xde\xe5\x39\x5d\x42\xfa\xbb\xf4\xae\xa0\xea\x7f\x8f\xb7\x10\xd4\x44\x97\x53\x42\xfa\x9d\x82\xaa\xcf\xdc\xef\x41\x73\xda\xa8\x98\x2c\xca\x30\x71\x1a\x1b\xe5\xdd\x63\x23\xbc\x7b\x6c\x74\xd6\xea\xa5\x49\x77\xe1\x4c\x98\x22\xd4\x41\x9d\xb4\x81\xcc\x4a\xfd\x8e\xa3\x38\xf3\xdf\xea\x4f\xf2\x57\x7d\xf0\x5f\x3b\xeb\x3c\xbb\x16\x9c\xf9\xa7\x78\x96\x81\xb3\xe0\x4d\x9e\xdd\xa4\xd7\xb3\x3c\x6b\xe8\xd5\xb3\x2a\x56\xd2\x4a\x58\x31\x8b\x2c\x97\x77\x8d\x8d\xcc\x5a\x16\xd9\x52\x78\x04\x55\x15\xde\x8d\x82\xfa\x43\x78\xd5\xdd\xea\x4f\xe1\xf9\xfb\xd5\x4d\xe1\xf9\xa6\xfa\x2b\xef\xaa\x2a\x60\x4b\x47\x09\xe9\x2f\x8a\xe5\x7a\x5d\xdd\xb2\x65\x49\x6b\x17\xe7\xb5\xbc\xbc\xa0\xe5\xca\x49\x2d\x7f\x7b\x4f\x09\x19\x6c\x3e\xa5\xe5\xb3\xb6\x9c\x73\x81\x26\xe3\x98\xab\x7d\x98\x04\xf0\x36\x30\x33\x04\xe2\x3d\x54\x08\x7b\xaa\x66\xcb\x1c\xa9\x5a\xc8\x05\xe5\x3f\x02\x03\x85\x5c\x20\xff\x21\x98\xc6\x73\xba\x51\xc8\x85\x54\x2d\xff\x59\x1d\xa0\xfc\x27\x3e\x40\xfb\x08\x34\x77\x01\x10\x2b\x24\x54\xad\xbc\xa4\x6a\x42\x06\x5b\x4d\x02\x71\xa6\x2e\xf3\x04\x15\x72\x23\xaa\x86\xd8\x31\x8d\x91\x01\x9c\xae\x52\x9c\xc7\xb1\x81\x0f\x82\xf1\x01\xb0\xe8\x16\x21\x83\xff\xe5\x92\xaa\xc6\x0f\x82\xad\x89\x6b\x7b\x44\x73\x0f\x96\x53\xb9\x84\xaa\x45\x63\x88\xdf\xe5\xf6\xdf\xcf\xe3\x9b\x49\xf0\x18\x89\x88\xa7\x6a\xfa\x5c\xd1\x90\x19\x03\xcd\x0e\xe9\xe8\x03\x09\xbe\xa7\x0e\xbe\xc3\xc7\x7f\xa8\x14\x2f\x85\x84\x0c\x1e\xd9\xaf\x73\x6d\x78\x75\x88\xc7\x6e\x14\x8e\x18\xba\x6e\x8b\x3a\x3b\x57\xe7\x69\xcb\x60\x7b\x30\x1e\xbc\x14\x4c\x38\x34\x68\x9c\x7b\x92\x3f\x8f\x7a\xf4\xf1\x7f\xaf\xd6\x89\xf2\x14\x02\x90\xa7\x7f\x7d\xa0\xb1\x9d\xd4\x4d\x11\x32\x93\x08\x99\x12\x34\x4f\x83\x86\x15\xb3\xa4\x95\x14\x32\x38\xf4\xe0\xde\xc2\xbd\x3d\x87\x22\x34\x68\xb4\x91\x15\x5b\xe8\xd2\x38\xdd\x03\x67\x7c\xce\x5d\xed\x66\x70\xda\x8c\x01\xcd\xbb\x1c\xbc\x6d\xcb\xf9\x01\x1e\x57\xb7\x47\x13\xa0\x92\xda\x21\x81\x79\x2d\x69\x41\xcb\xd0\x49\x2d\xc3\xb6\x3c\xe2\x56\xdb\xef\xf1\x3f\xd5\xbd\xad\x76\x34\x6f\x43\xb0\x68\x4b\x1d\x41\xfc\xa8\x6d\xcd\x28\x25\x3a\x62\x54\x3b\x9c\xf1\xb9\x66\x3d\x17\xdb\x1b\xd6\x63\xf7\x49\x7d\xf6\x5e\x82\x77\xf2\xfb\xb6\x6f\x7b\x50\x3a\xcb\x2f\xd4\xeb\x42\x06\x57\x7b\xd8\xb9\x27\xd6\xe9\xda\x5e\x15\x32\xf8\xee\xff\x64\xb9\x3a\x23\x5b\x7e\x41\x4e\x8c\xb3\xe0\xeb\xb0\x57\x5d\x63\xcb\x85\x96\xd6\x61\x4b\x41\x37\x1f\x12\x34\xbd\x5d\xd0\xba\x90\xa0\xf2\x16\x41\xfd\x93\x9c\xa9\xf7\x6d\x4f\xdd\x7a\x37\xb5\x7a\x73\x5b\xdd\x68\xce\xa6\x5a\x6e\x4d\x6f\x43\xbf\xbe\xaa\xdb\xb2\x75\x22\xf5\x54\x0f\xe3\x4c\x0d\xe9\xbc\x55\x6f\xce\xab\xb6\x97\x97\xaa\xed\xf3\x5f\xf2\x25\x75\xbb\xc7\x03\x1e\xf6\x40\xe7\x57\x74\x25\x55\xaf\x2d\x9d\x0a\xef\x1a\xf8\x8a\x0f\xed\xfa\x89\x57\x82\xe7\x6c\xa9\x67\x77\xfe\x4c\x1f\x6d\xa0\x0e\x32\x3d\x84\xcc\x0a\xc8\x4c\xe8\x6e\x08\xa9\xec\xfe\x15\x21\xd5\x9e\xfe\x2b\x42\xaa\x17\xb5\xbe\x6e\x38\xba\xac\xd7\x6d\x3f\x0b\xa9\xc6\xfb\xa8\x34\x12\x5d\x8a\xfe\x62\x25\xb6\x5d\x6b\xda\x85\x54\x2f\x5b\x09\xab\x62\x79\x96\xbb\xea\xbb\xf9\x2e\xf4\x35\x72\xd3\x9e\xf9\x28\x58\xc7\x25\x55\x13\x57\xf4\x94\x14\x3d\x55\x8b\x7a\x6d\x4b\x11\xea\x23\x41\x54\xe8\xd8\xc8\x99\x7a\xfa\x9d\x14\x8f\x53\x21\x42\xd1\x8d\x56\x22\x3a\x24\xa4\x3a\x31\x1b\x2b\xc5\x85\x54\xe5\x56\x8d\x2c\x6f\xf5\xbd\x78\x30\x52\x37\x75\x52\x1f\xad\x47\xe9\xd2\x7a\x66\x76\xa1\xcd\x1c\x42\xd8\x1c\x47\xc8\xcc\x82\xcc\x13\xba\xdb\xea\x73\x47\x9f\xf6\xa2\x90\xea\xfc\x7a\xb7\x61\xf1\xf2\x6e\xbd\x0e\x58\x9e\x95\xb4\x64\xcb\x6f\x98\x66\xbb\x84\x54\x3f\xe8\xb9\x51\xdf\x58\x61\x5b\x1a\xc3\x46\xd2\xc8\x1a\x69\x63\xd8\xc8\x18\x53\x46\xda\x98\x36\x98\xb1\xcb\xd8\x6b\x30\xe3\x51\x63\xd8\x18\x6e\xfc\x3f\x06\xb0\x9d\x6c\x27\x63\x6c\x2f\xcb\xb0\x49\x76\x88\xcd\xb0\x09\x96\x61\x53\x6c\x1a\x0f\x7c\x3a\xe6\x3f\xfa\x76\x23\x8b\x34\xa6\x31\x0d\x86\x24\xa6\x70\x18\x0c\xaf\x21\xdd\x58\x67\x90\x01\x6b\xec\xcf\x20\x8b\xc3\x00\x5e\x68\x58\x52\xc8\x20\x83\x19\x24\x91\xc2\x24\xd2\x60\x78\x1d\x69\x1c\xc0\x04\x92\x60\x78\x05\x19\x1c\xc2\x14\x0e\xdc\xc3\x1f\x40\x16\x13\x78\xa3\x11\x87\xc1\x46\x06\x53\x98\x42\x1a\x36\x66\x1a\x3c\xfc\x1d\x00\x00\xff\xff\x2b\x87\xc4\x0f\xb8\x06\x00\x00") 72 | 73 | func bbosBinBytes() ([]byte, error) { 74 | return bindataRead( 75 | _bbosBin, 76 | "bbos.bin", 77 | ) 78 | } 79 | 80 | func bbosBin() (*asset, error) { 81 | bytes, err := bbosBinBytes() 82 | if err != nil { 83 | return nil, err 84 | } 85 | 86 | info := bindataFileInfo{name: "bbos.bin", size: 1720, mode: os.FileMode(436), modTime: time.Unix(1485529242, 0)} 87 | a := &asset{bytes: bytes, info: info} 88 | return a, nil 89 | } 90 | 91 | // Asset loads and returns the asset for the given name. 92 | // It returns an error if the asset could not be found or 93 | // could not be loaded. 94 | func Asset(name string) ([]byte, error) { 95 | cannonicalName := strings.Replace(name, "\\", "/", -1) 96 | if f, ok := _bindata[cannonicalName]; ok { 97 | a, err := f() 98 | if err != nil { 99 | return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) 100 | } 101 | return a.bytes, nil 102 | } 103 | return nil, fmt.Errorf("Asset %s not found", name) 104 | } 105 | 106 | // MustAsset is like Asset but panics when Asset would return an error. 107 | // It simplifies safe initialization of global variables. 108 | func MustAsset(name string) []byte { 109 | a, err := Asset(name) 110 | if err != nil { 111 | panic("asset: Asset(" + name + "): " + err.Error()) 112 | } 113 | 114 | return a 115 | } 116 | 117 | // AssetInfo loads and returns the asset info for the given name. 118 | // It returns an error if the asset could not be found or 119 | // could not be loaded. 120 | func AssetInfo(name string) (os.FileInfo, error) { 121 | cannonicalName := strings.Replace(name, "\\", "/", -1) 122 | if f, ok := _bindata[cannonicalName]; ok { 123 | a, err := f() 124 | if err != nil { 125 | return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) 126 | } 127 | return a.info, nil 128 | } 129 | return nil, fmt.Errorf("AssetInfo %s not found", name) 130 | } 131 | 132 | // AssetNames returns the names of the assets. 133 | func AssetNames() []string { 134 | names := make([]string, 0, len(_bindata)) 135 | for name := range _bindata { 136 | names = append(names, name) 137 | } 138 | return names 139 | } 140 | 141 | // _bindata is a table, holding each asset generator, mapped to its name. 142 | var _bindata = map[string]func() (*asset, error){ 143 | "bbos.bin": bbosBin, 144 | } 145 | 146 | // AssetDir returns the file names below a certain 147 | // directory embedded in the file by go-bindata. 148 | // For example if you run go-bindata on data/... and data contains the 149 | // following hierarchy: 150 | // data/ 151 | // foo.txt 152 | // img/ 153 | // a.png 154 | // b.png 155 | // then AssetDir("data") would return []string{"foo.txt", "img"} 156 | // AssetDir("data/img") would return []string{"a.png", "b.png"} 157 | // AssetDir("foo.txt") and AssetDir("notexist") would return an error 158 | // AssetDir("") will return []string{"data"}. 159 | func AssetDir(name string) ([]string, error) { 160 | node := _bintree 161 | if len(name) != 0 { 162 | cannonicalName := strings.Replace(name, "\\", "/", -1) 163 | pathList := strings.Split(cannonicalName, "/") 164 | for _, p := range pathList { 165 | node = node.Children[p] 166 | if node == nil { 167 | return nil, fmt.Errorf("Asset %s not found", name) 168 | } 169 | } 170 | } 171 | if node.Func != nil { 172 | return nil, fmt.Errorf("Asset %s not found", name) 173 | } 174 | rv := make([]string, 0, len(node.Children)) 175 | for childName := range node.Children { 176 | rv = append(rv, childName) 177 | } 178 | return rv, nil 179 | } 180 | 181 | type bintree struct { 182 | Func func() (*asset, error) 183 | Children map[string]*bintree 184 | } 185 | 186 | var _bintree = &bintree{nil, map[string]*bintree{ 187 | "bbos.bin": &bintree{bbosBin, map[string]*bintree{}}, 188 | }} 189 | 190 | // RestoreAsset restores an asset under the given directory 191 | func RestoreAsset(dir, name string) error { 192 | data, err := Asset(name) 193 | if err != nil { 194 | return err 195 | } 196 | info, err := AssetInfo(name) 197 | if err != nil { 198 | return err 199 | } 200 | err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) 201 | if err != nil { 202 | return err 203 | } 204 | err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) 205 | if err != nil { 206 | return err 207 | } 208 | err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) 209 | if err != nil { 210 | return err 211 | } 212 | return nil 213 | } 214 | 215 | // RestoreAssets restores an asset under the given directory recursively 216 | func RestoreAssets(dir, name string) error { 217 | children, err := AssetDir(name) 218 | // File 219 | if err != nil { 220 | return RestoreAsset(dir, name) 221 | } 222 | // Dir 223 | for _, child := range children { 224 | err = RestoreAssets(dir, filepath.Join(name, child)) 225 | if err != nil { 226 | return err 227 | } 228 | } 229 | return nil 230 | } 231 | 232 | func _filePath(dir, name string) string { 233 | cannonicalName := strings.Replace(name, "\\", "/", -1) 234 | return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) 235 | } 236 | -------------------------------------------------------------------------------- /GEMUSingle/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "image" 7 | "image/color" 8 | "log" 9 | "os" 10 | "strings" 11 | "time" 12 | 13 | "github.com/techcompliant/GEMU" 14 | "github.com/andyleap/tinyfb" 15 | ) 16 | 17 | func GetColor(c uint16) color.Color { 18 | return color.RGBA{ 19 | R: uint8((c >> 8 & 0x0f) << 4), 20 | G: uint8((c >> 4 & 0x0f) << 4), 21 | B: uint8((c >> 0 & 0x0f) << 4), 22 | } 23 | } 24 | 25 | var RomImage = flag.String("rom", "internal/bbos.bin", "Filename of rom image to use (internal bbos by default)") 26 | var RomFlip = flag.Bool("noromflip", false, "Don't endian flip the rom") 27 | 28 | type FloppyImages []string 29 | 30 | func (fi *FloppyImages) String() string { 31 | return fmt.Sprintf("%s", *fi) 32 | } 33 | 34 | func (fi *FloppyImages) Set(value string) error { 35 | *fi = append(*fi, value) 36 | return nil 37 | } 38 | 39 | func main() { 40 | fis := &FloppyImages{} 41 | flag.Var(fis, "floppy", "Floppy images to use (can specify multiple times for multiple drives)") 42 | 43 | flag.Parse() 44 | 45 | t := tinyfb.New("DCPU", (128+12)*4, (96+12)*4) 46 | go func() { 47 | t.Run() 48 | os.Exit(0) 49 | }() 50 | 51 | gemu.SetStorage(gemu.NewMultiStorage(AssetStorage{Root: "internal/"}, gemu.NewDiskStorage("."))) 52 | 53 | cpu := gemu.NewDCPU(0) 54 | 55 | rom := gemu.NewRom(*RomImage, !*RomFlip) 56 | cpu.Attach(rom) 57 | rom.SetUp(cpu) 58 | 59 | clock := gemu.NewClock() 60 | cpu.Attach(clock) 61 | clock.SetUp(cpu) 62 | 63 | lem := gemu.NewLem1802() 64 | cpu.Attach(lem) 65 | lem.SetUp(cpu) 66 | 67 | keyboard := gemu.NewKeyboard() 68 | cpu.Attach(keyboard) 69 | keyboard.SetUp(cpu) 70 | 71 | floppies := []*gemu.M35FD{} 72 | 73 | for _, fi := range *fis { 74 | floppy := gemu.NewM35FD(true) 75 | cpu.Attach(floppy) 76 | floppy.SetUp(cpu) 77 | floppy.ChangeDisk(fi) 78 | floppies = append(floppies, floppy) 79 | } 80 | 81 | cpu.Start() 82 | 83 | lemImageBig := image.NewRGBA(image.Rect(0, 0, (128+12)*4, (96+12)*4)) 84 | 85 | t.Char(func(char string, mods int) { 86 | switch char { 87 | case "BackSpace", "\x08": 88 | char = "\x10" 89 | case "Return", "\x0d": 90 | char = "\x11" 91 | case "Insert": 92 | char = "\x12" 93 | case "Delete": 94 | char = "\x13" 95 | case "Up": 96 | char = "\x80" 97 | case "Down": 98 | char = "\x81" 99 | case "Left": 100 | char = "\x82" 101 | case "Right": 102 | char = "\x83" 103 | } 104 | 105 | if len(char) == 1 { 106 | keyboard.ParsedKey(uint16(char[0])) 107 | } else { 108 | //log.Println("Got: ", char) 109 | } 110 | }) 111 | 112 | t.Key(func(key string, mods int, press bool) { 113 | switch key { 114 | case "BackSpace", "\x08": 115 | key = "\x10" 116 | case "Return", "\x0d": 117 | key = "\x11" 118 | case "Insert": 119 | key = "\x12" 120 | case "Delete": 121 | key = "\x13" 122 | } 123 | keyboard.RawKey(uint16(key[0]), press) 124 | }) 125 | 126 | go func() { 127 | for { 128 | time.Sleep(20 * time.Millisecond) 129 | 130 | if lem.DspMem == 0 { 131 | continue 132 | } 133 | dspRam := lem.GetMem().GetRaw()[lem.DspMem:] 134 | fontRam := gemu.LemDefFont 135 | if lem.FontMem != 0 { 136 | fontRam = lem.GetMem().GetRaw()[lem.FontMem:] 137 | } 138 | palRam := gemu.LemDefPal 139 | if lem.PalMem != 0 { 140 | palRam = lem.GetMem().GetRaw()[lem.PalMem:] 141 | } 142 | cl := GetColor(palRam[lem.Border&0xf]) 143 | for x := 0; x < (128+12)*4; x++ { 144 | for y := 0; y < 6*4; y++ { 145 | lemImageBig.Set(x, y, cl) 146 | lemImageBig.Set(x, ((96+12)*4)-y, cl) 147 | } 148 | } 149 | for x := 0; x < (6)*4; x++ { 150 | for y := 0; y < (96+12)*4; y++ { 151 | lemImageBig.Set(x, y, cl) 152 | lemImageBig.Set((128+12)*4-x, y, cl) 153 | } 154 | } 155 | 156 | for ty := 0; ty < 12; ty++ { 157 | vtb := uint16(0x0100) 158 | vta := uint16(0x0001) 159 | for y := 0; y < 8; y++ { 160 | for x := 0; x < 32; x++ { 161 | vtw := dspRam[x+ty*32] 162 | clb := palRam[(vtw>>8)&0x0f] 163 | clf := palRam[(vtw>>12)&0x0f] 164 | vtw &= 0x7f 165 | vtw <<= 1 166 | f := fontRam[vtw] 167 | cl := clf 168 | if f&vtb == 0 { 169 | cl = clb 170 | } 171 | for sx := 0; sx < 4; sx++ { 172 | for sy := 0; sy < 4; sy++ { 173 | lemImageBig.Set(((x*4)+0)*4+sx+6*4, (ty*8+y)*4+sy+6*4, GetColor(cl)) 174 | } 175 | } 176 | 177 | f = fontRam[vtw] 178 | cl = clf 179 | if f&vta == 0 { 180 | cl = clb 181 | } 182 | for sx := 0; sx < 4; sx++ { 183 | for sy := 0; sy < 4; sy++ { 184 | lemImageBig.Set(((x*4)+1)*4+sx+6*4, (ty*8+y)*4+sy+6*4, GetColor(cl)) 185 | } 186 | } 187 | 188 | f = fontRam[vtw+1] 189 | cl = clf 190 | if f&vtb == 0 { 191 | cl = clb 192 | } 193 | for sx := 0; sx < 4; sx++ { 194 | for sy := 0; sy < 4; sy++ { 195 | lemImageBig.Set(((x*4)+2)*4+sx+6*4, (ty*8+y)*4+sy+6*4, GetColor(cl)) 196 | } 197 | } 198 | 199 | f = fontRam[vtw+1] 200 | cl = clf 201 | if f&vta == 0 { 202 | cl = clb 203 | } 204 | for sx := 0; sx < 4; sx++ { 205 | for sy := 0; sy < 4; sy++ { 206 | lemImageBig.Set(((x*4)+3)*4+sx+6*4, (ty*8+y)*4+sy+6*4, GetColor(cl)) 207 | } 208 | } 209 | 210 | } 211 | vtb <<= 1 212 | vta <<= 1 213 | } 214 | } 215 | t.Update(lemImageBig) 216 | } 217 | }() 218 | 219 | for { 220 | cpu.Tick(1) 221 | clock.Tick(1) 222 | for _, fd := range floppies { 223 | fd.Tick(1) 224 | } 225 | } 226 | } 227 | 228 | type AssetStorage struct { 229 | Root string 230 | } 231 | 232 | func (a AssetStorage) Exists(Item string) bool { 233 | if strings.HasPrefix(Item, a.Root) { 234 | Item = strings.TrimPrefix(Item, a.Root) 235 | } else { 236 | return false 237 | } 238 | _, err := Asset(Item) 239 | if err != nil { 240 | return false 241 | } 242 | return true 243 | } 244 | func (a AssetStorage) Length(Item string) int { 245 | if strings.HasPrefix(Item, a.Root) { 246 | Item = strings.TrimPrefix(Item, a.Root) 247 | } else { 248 | return 0 249 | } 250 | asset, err := AssetInfo(Item) 251 | if err != nil { 252 | return 0 253 | } 254 | return int(asset.Size()) 255 | } 256 | func (a AssetStorage) Read(Item string, offset int, data []byte) { 257 | if strings.HasPrefix(Item, a.Root) { 258 | Item = strings.TrimPrefix(Item, a.Root) 259 | } else { 260 | return 261 | } 262 | asset, err := Asset(Item) 263 | if err != nil { 264 | return 265 | } 266 | copy(data, asset[offset:]) 267 | } 268 | func (a AssetStorage) Write(Item string, offset int, data []byte) { 269 | if strings.HasPrefix(Item, a.Root) { 270 | 271 | } else { 272 | return 273 | } 274 | log.Println("Writing to internal assets is not supported!") 275 | } 276 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL: GEMUSingle 2 | 3 | .PHONY: GEMUSingle 4 | GEMUSingle: 5 | cd GEMUSingle && make 6 | .PHONY: GEMUSingle_clean 7 | GEMUSingle_clean: 8 | cd GEMUSingle && make clean 9 | .PHONY: GEMUSingle_install 10 | GEMUSingle_install: 11 | cd GEMUSingle && make install 12 | 13 | .PHONY: clean 14 | clean: GEMUSingle_clean 15 | .PHONY: install 16 | install: GEMUSingle_install 17 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://godoc.org/gemu.techcompliant.com/gemu?status.svg)](https://godoc.org/gemu.techcompliant.com/gemu) 2 | 3 | # Introduction 4 | 5 | GEMU is the DCPU emulator that powers Tech Compliant. 6 | In order to allow DCPU developers to write software targeting our DCPU systems easier, we are open sourcing the core of our emulator, as well as a few small utilities to allow it to be used directly, without requiring developers to build their own tools around our emulator. 7 | 8 | # Documentation 9 | 10 | GEMU documentation is currently being built, though a very minimal amount of information can be gathered by the simple godoc tool. This can be accessed via the GoDoc tag above. 11 | 12 | # GEMUSingle 13 | 14 | Included in this repo is a simple single DCPU emulator. If you have installed Go correctly, and set up a proper gopath, this can be compiled via `make` either from this main directory, or from in the GEMUSingle directory. Of course, if you are more comfortable with the `go` tool, feel free to use it directly. 15 | 16 | # GEMU Compatible projects 17 | 18 | The following is a short list of projects that are confirmed to be working with GEMU. Note that TC has changed a few device IDs, specifically the LEM and keyboard IDs, so stock DCPU code may not run directly on this emulator. 19 | 20 | * [BBOS](https://github.com/madmockers/BareBonesOS) 21 | * [Admiral](https://github.com/orlof/dcpu-admiral) 22 | * [MoonPatrol](https://github.com/orlof/dcpu-moonpatrol) 23 | * [DCPU-MUD](https://github.com/orlof/dcpu-mud) 24 | * [DC-DOS](https://github.com/interfect/bbfs) 25 | * -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | GEMU is the DCPU emulator that powers Tech Compliant. 3 | In order to allow DCPU developers to write software targeting our DCPU systems easier, we are open sourcing the core of our emulator, as well as a few small utilities to allow it to be used directly, without requiring developers to build their own tools around our emulator. 4 | */ 5 | package gemu 6 | -------------------------------------------------------------------------------- /hardware.go: -------------------------------------------------------------------------------- 1 | package gemu 2 | 3 | type Hardware struct { 4 | Up IHardware 5 | Down []IHardware 6 | Class *HardwareClass 7 | } 8 | 9 | func (H *Hardware) GetClass() *HardwareClass { 10 | return H.Class 11 | } 12 | 13 | func (H *Hardware) GetUp() IHardware { 14 | return H.Up 15 | } 16 | 17 | func (H *Hardware) GetMem() IMem { 18 | if H.Up != nil { 19 | if dcpu, ok := H.Up.(*DCPU); ok { 20 | return dcpu.Mem 21 | } 22 | } 23 | return nil 24 | } 25 | 26 | func (H *Hardware) SetUp(up IHardware) { 27 | H.Up = up 28 | } 29 | 30 | func (H *Hardware) Attach(Add IHardware) { 31 | H.Down = append(H.Down, Add) 32 | } 33 | 34 | func (H *Hardware) GetDown() []IHardware { 35 | return H.Down 36 | } 37 | 38 | func (H *Hardware) Start() { 39 | for _, obj := range H.Down { 40 | obj.Start() 41 | } 42 | } 43 | 44 | func (H *Hardware) Stop() { 45 | for _, obj := range H.Down { 46 | obj.Stop() 47 | } 48 | } 49 | 50 | func (H *Hardware) Reset() { 51 | for _, obj := range H.Down { 52 | obj.Reset() 53 | } 54 | } 55 | 56 | func (H *Hardware) HWI(D *DCPU) { 57 | //log.Printf("Unhandled HWI\n") 58 | } 59 | 60 | func (H *Hardware) HWQ(D *DCPU) { 61 | C := H.GetClass() 62 | D.Reg[0] = uint16(C.DevID & 0xFFFF) 63 | D.Reg[1] = uint16((C.DevID >> 16) & 0xFFFF) 64 | D.Reg[2] = C.VerID 65 | D.Reg[3] = uint16(C.MfgID & 0xFFFF) 66 | D.Reg[4] = uint16((C.MfgID >> 16) & 0xFFFF) 67 | } 68 | 69 | type IHardware interface { 70 | GetUp() IHardware 71 | GetMem() IMem 72 | Attach(Add IHardware) 73 | GetDown() []IHardware 74 | SetUp(up IHardware) 75 | GetClass() *HardwareClass 76 | Start() 77 | Stop() 78 | Reset() 79 | HWI(D *DCPU) 80 | HWQ(D *DCPU) 81 | } 82 | 83 | type Ticker interface { 84 | Tick(int) 85 | } 86 | 87 | type HardwareClass struct { 88 | Name string 89 | Desc string 90 | DevID uint32 91 | VerID uint16 92 | MfgID uint32 93 | } 94 | 95 | type IStateChanges interface { 96 | IsDirty() bool 97 | ClearDirty() 98 | } 99 | 100 | var Classes []*HardwareClass 101 | 102 | func RegisterClass(hc *HardwareClass) { 103 | Classes = append(Classes, hc) 104 | } 105 | 106 | type IMem interface { 107 | ReadMem(addr uint16) uint16 108 | WriteMem(addr uint16, val uint16) 109 | LoadMem(data []uint16) 110 | GetRaw() []uint16 111 | RegisterSync(addr uint16, synclen uint16) *Sync 112 | Reset() 113 | } 114 | -------------------------------------------------------------------------------- /hardware_clock.go: -------------------------------------------------------------------------------- 1 | package gemu 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | var clockClass = &HardwareClass{ 8 | Name: "clock", 9 | Desc: "Generic Clock", 10 | DevID: 0x12d0b402, 11 | VerID: 0x0002, 12 | MfgID: 0x1c6c8b36, 13 | } 14 | 15 | func init() { 16 | RegisterClass(clockClass) 17 | } 18 | 19 | type Clock struct { 20 | Hardware 21 | Rate uint16 22 | RateAccum uint16 23 | Total uint16 24 | Accum uint16 25 | TicksLeft int 26 | Interrupt uint16 27 | RealOffset time.Duration 28 | RunTimeStart time.Time 29 | } 30 | 31 | func NewClock() *Clock { 32 | dev := &Clock{} 33 | dev.Class = clockClass 34 | realTime := time.Date(2600, 35 | time.January, 36 | 1, 37 | 0, 38 | 0, 39 | 0, 40 | 0, 41 | time.UTC) 42 | dev.RealOffset = realTime.Sub(time.Now()) 43 | dev.RunTimeStart = time.Now() 44 | return dev 45 | } 46 | 47 | func (c *Clock) HWI(D *DCPU) { 48 | switch D.Reg[0] { 49 | case 0: 50 | c.Rate = D.Reg[1] 51 | case 1: 52 | D.Reg[2] = c.Total 53 | c.Total = 0 54 | case 2: 55 | c.Interrupt = D.Reg[1] 56 | case 0x0010: 57 | realTime := time.Now().Add(c.RealOffset) 58 | D.Reg[1] = uint16(realTime.Year()) 59 | D.Reg[2] = uint16(int(realTime.Month())<<8 | realTime.Day()) 60 | D.Reg[3] = uint16(realTime.Hour()<<8 | realTime.Minute()) 61 | D.Reg[4] = uint16(realTime.Second()) 62 | D.Reg[5] = uint16(realTime.Nanosecond() / int(time.Millisecond)) 63 | case 0x0011: 64 | runTimeTotal := time.Now().Sub(c.RunTimeStart) 65 | D.Reg[2] = uint16(runTimeTotal / (time.Hour * 24)) 66 | D.Reg[3] = uint16(runTimeTotal/(time.Hour))<<8 | uint16(runTimeTotal/(time.Minute)) 67 | D.Reg[4] = uint16(runTimeTotal / (time.Second)) 68 | D.Reg[5] = uint16(runTimeTotal / (time.Millisecond)) 69 | case 0x0012: 70 | realTime := time.Date(int(D.Reg[1]), 71 | time.Month(D.Reg[2]>>8), 72 | int(D.Reg[2]&0xFF), 73 | int(D.Reg[3]>>8), 74 | int(D.Reg[3]&0xFF), 75 | int(D.Reg[4]), 76 | int(D.Reg[5])*int(time.Millisecond), 77 | time.UTC) 78 | c.RealOffset = realTime.Sub(time.Now()) 79 | case 0xFFFF: 80 | c.Reset() 81 | } 82 | } 83 | 84 | func (c *Clock) Reset() { 85 | c.RunTimeStart = time.Now() 86 | c.Rate = 0 87 | c.Total = 0 88 | c.Interrupt = 0 89 | } 90 | 91 | func (c *Clock) Tick(ticks int) { 92 | c.TicksLeft -= ticks 93 | for c.TicksLeft < 0 { 94 | if c.Accum < 15 { 95 | c.Accum++ 96 | c.TicksLeft += 1666 97 | } else { 98 | c.Accum = 0 99 | c.TicksLeft += 1676 100 | } 101 | c.RateAccum++ 102 | if c.RateAccum >= c.Rate { 103 | c.RateAccum = 0 104 | if c.Rate > 0 { 105 | c.Total++ 106 | if c.Interrupt != 0 && c.Up != nil { 107 | if dcpu, ok := c.Up.(*DCPU); ok { 108 | dcpu.Int(c.Interrupt) 109 | } 110 | } 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /hardware_dcpu.go: -------------------------------------------------------------------------------- 1 | package gemu 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "unsafe" 7 | ) 8 | 9 | type Instruction struct { 10 | Opcode uint16 11 | OpA uint16 12 | AddA uint16 13 | OpB uint16 14 | AddB uint16 15 | SPBShift bool 16 | } 17 | 18 | func OperandString(op uint16, add uint16) string { 19 | switch op { 20 | case 0x00: 21 | return "A" 22 | case 0x01: 23 | return "B" 24 | case 0x02: 25 | return "C" 26 | case 0x03: 27 | return "X" 28 | case 0x04: 29 | return "Y" 30 | case 0x05: 31 | return "Z" 32 | case 0x06: 33 | return "I" 34 | case 0x07: 35 | return "J" 36 | case 0x08: 37 | return "[A]" 38 | case 0x09: 39 | return "[B]" 40 | case 0x0A: 41 | return "[C]" 42 | case 0x0B: 43 | return "[X]" 44 | case 0x0C: 45 | return "[Y]" 46 | case 0x0D: 47 | return "[Z]" 48 | case 0x0E: 49 | return "[I]" 50 | case 0x0F: 51 | return "[J]" 52 | case 0x10: 53 | return fmt.Sprintf("[A+%04x]", add) 54 | case 0x11: 55 | return fmt.Sprintf("[B+%04x]", add) 56 | case 0x12: 57 | return fmt.Sprintf("[C+%04x]", add) 58 | case 0x13: 59 | return fmt.Sprintf("[X+%04x]", add) 60 | case 0x14: 61 | return fmt.Sprintf("[Y+%04x]", add) 62 | case 0x15: 63 | return fmt.Sprintf("[Z+%04x]", add) 64 | case 0x16: 65 | return fmt.Sprintf("[I+%04x]", add) 66 | case 0x17: 67 | return fmt.Sprintf("[J+%04x]", add) 68 | case 0x18: 69 | return "PSHPOP" 70 | case 0x19: 71 | return "[SP]" 72 | case 0x1a: 73 | return fmt.Sprintf("[SP+%04x]", add) 74 | case 0x1b: 75 | return "SP" 76 | case 0x1C: 77 | return "PC" 78 | case 0x1D: 79 | return "EX" 80 | case 0x1E: 81 | return fmt.Sprintf("[%04x]", add) 82 | case 0x1F: 83 | return fmt.Sprintf("%04x", add) 84 | default: 85 | return fmt.Sprintf("%04x", op-0x21) 86 | } 87 | } 88 | 89 | func (I *Instruction) String() string { 90 | Inst := fmt.Sprintf("ERR:%02x", I.Opcode) 91 | if I.Opcode != 0 { 92 | switch I.Opcode { 93 | case 0x01: 94 | Inst = "SET" 95 | case 0x02: 96 | Inst = "ADD" 97 | case 0x03: 98 | Inst = "SUB" 99 | case 0x04: 100 | Inst = "MUL" 101 | case 0x05: 102 | Inst = "MLI" 103 | case 0x06: 104 | Inst = "DIV" 105 | case 0x07: 106 | Inst = "DVI" 107 | case 0x08: 108 | Inst = "MOD" 109 | case 0x09: 110 | Inst = "MDI" 111 | case 0x0A: 112 | Inst = "AND" 113 | case 0x0B: 114 | Inst = "BOR" 115 | case 0x0C: 116 | Inst = "XOR" 117 | case 0x0D: 118 | Inst = "SHR" 119 | case 0x0E: 120 | Inst = "ASR" 121 | case 0x0F: 122 | Inst = "SHL" 123 | case 0x10: 124 | Inst = "IFB" 125 | case 0x11: 126 | Inst = "IFC" 127 | case 0x12: 128 | Inst = "IFE" 129 | case 0x13: 130 | Inst = "IFN" 131 | case 0x14: 132 | Inst = "IFG" 133 | case 0x15: 134 | Inst = "IFA" 135 | case 0x16: 136 | Inst = "IFL" 137 | case 0x17: 138 | Inst = "IFU" 139 | case 0x1A: 140 | Inst = "ADX" 141 | case 0x1B: 142 | Inst = "SBX" 143 | case 0x1E: 144 | Inst = "STI" 145 | case 0x1F: 146 | Inst = "STD" 147 | } 148 | Inst += " " + OperandString(I.OpB, I.AddB) + " " + OperandString(I.OpA, I.AddA) 149 | } else { 150 | Inst = fmt.Sprintf("SPC:%02x", I.OpB) 151 | switch I.OpB { 152 | case 0x01: 153 | Inst = "JSR" 154 | case 0x08: 155 | Inst = "INT" 156 | case 0x09: 157 | Inst = "IAG" 158 | case 0x0A: 159 | Inst = "IAS" 160 | case 0x0B: 161 | Inst = "RFI" 162 | case 0x0C: 163 | Inst = "IAQ" 164 | case 0x10: 165 | Inst = "HWN" 166 | case 0x11: 167 | Inst = "HWQ" 168 | case 0x12: 169 | Inst = "HWI" 170 | case 0x13: 171 | Inst = "LOG" 172 | case 0x14: 173 | Inst = "BRK" 174 | case 0x15: 175 | Inst = "HLT" 176 | } 177 | Inst += " " + OperandString(I.OpA, I.AddA) 178 | } 179 | return Inst 180 | } 181 | 182 | func Decode(inst uint16) Instruction { 183 | ret := Instruction{} 184 | ret.Opcode = inst & 0x001F 185 | ret.OpA = inst >> 10 186 | ret.OpB = (inst >> 5) & 0x001F 187 | return ret 188 | } 189 | 190 | func (I *Instruction) PreProcessOps(D *DCPU) { 191 | switch { 192 | case I.OpA >= 0x10 && I.OpA <= 0x17: 193 | fallthrough 194 | case I.OpA == 0x1a || I.OpA == 0x1e || I.OpA == 0x1f: 195 | I.AddA = D.Mem.ReadMem(D.PC) 196 | D.PC++ 197 | } 198 | if I.Opcode != 0 { 199 | switch { 200 | case I.OpB >= 0x10 && I.OpB <= 0x17: 201 | fallthrough 202 | case I.OpB == 0x1a || I.OpB == 0x1e || I.OpB == 0x1f: 203 | I.AddB = D.Mem.ReadMem(D.PC) 204 | D.PC++ 205 | } 206 | } 207 | } 208 | 209 | func (I *Instruction) GetOp(D *DCPU, OpB bool) (val uint16) { 210 | Op := I.OpA 211 | Add := I.AddA 212 | if OpB { 213 | Op = I.OpB 214 | Add = I.AddB 215 | } 216 | switch { 217 | case Op <= 0x07: 218 | return D.Reg[Op] 219 | case Op <= 0x0F: 220 | return D.Mem.ReadMem(D.Reg[Op-0x08]) 221 | case Op <= 0x17: 222 | D.WaitState++ 223 | return D.Mem.ReadMem(D.Reg[Op-0x10] + Add) 224 | case Op == 0x18: 225 | if OpB { 226 | I.SPBShift = true 227 | D.SP-- 228 | return D.Mem.ReadMem(D.SP) 229 | } else { 230 | D.SP++ 231 | return D.Mem.ReadMem(D.SP - 1) 232 | } 233 | case Op == 0x19: 234 | return D.Mem.ReadMem(D.SP) 235 | case Op <= 0x1A: 236 | D.WaitState++ 237 | return D.Mem.ReadMem(D.SP + Add) 238 | case Op == 0x1B: 239 | return D.SP 240 | case Op == 0x1C: 241 | return D.PC 242 | case Op == 0x1D: 243 | return D.EX 244 | case Op == 0x1E: 245 | D.WaitState++ 246 | return D.Mem.ReadMem(Add) 247 | case Op == 0x1F: 248 | D.WaitState++ 249 | return Add 250 | case Op <= 0x3F: 251 | return Op - 0x21 252 | } 253 | panic(fmt.Sprintf("Invalid operand: %s", I.String())) 254 | } 255 | 256 | func (I *Instruction) SetOp(D *DCPU, val uint16) { 257 | Op := I.OpB 258 | Add := I.AddB 259 | switch { 260 | case Op <= 0x07: 261 | D.Reg[Op] = val 262 | return 263 | case Op <= 0x0F: 264 | D.Mem.WriteMem(D.Reg[Op-0x08], val) 265 | return 266 | case Op <= 0x17: 267 | D.Mem.WriteMem(D.Reg[Op-0x10]+Add, val) 268 | D.WaitState++ 269 | return 270 | case Op == 0x18: 271 | if !I.SPBShift { 272 | D.SP-- 273 | } 274 | D.Mem.WriteMem(D.SP, val) 275 | return 276 | case Op == 0x19: 277 | D.Mem.WriteMem(D.SP, val) 278 | return 279 | case Op <= 0x1A: 280 | D.Mem.WriteMem(D.SP+Add, val) 281 | D.WaitState++ 282 | return 283 | case Op == 0x1B: 284 | D.SP = val 285 | return 286 | case Op == 0x1C: 287 | D.PC = val 288 | return 289 | case Op == 0x1D: 290 | D.EX = val 291 | return 292 | case Op == 0x1E: 293 | D.Mem.WriteMem(Add, val) 294 | D.WaitState++ 295 | return 296 | case Op == 0x1f: 297 | return 298 | } 299 | 300 | panic(fmt.Sprintf("Invalid operand: %s", I.String())) 301 | } 302 | 303 | func (I *Instruction) Run(D *DCPU) { 304 | I.PreProcessOps(D) 305 | if D.Skipping { 306 | if I.Opcode >= 0x10 && I.Opcode <= 0x17 { 307 | return 308 | } 309 | D.Skipping = false 310 | return 311 | } 312 | switch I.Opcode { 313 | case 0x01: // SET 314 | I.SetOp(D, I.GetOp(D, false)) 315 | case 0x02: // ADD 316 | D.WaitState = 1 317 | a, b := uint32(I.GetOp(D, false)), uint32(I.GetOp(D, true)) 318 | val := b + a 319 | I.SetOp(D, uint16(val&0xFFFF)) 320 | D.EX = 0 321 | if val > 0xFFFF { 322 | D.EX = 1 323 | } 324 | case 0x03: // SUB 325 | D.WaitState = 1 326 | a, b := int32(I.GetOp(D, false)), int32(I.GetOp(D, true)) 327 | val := b - a 328 | I.SetOp(D, uint16(val)) 329 | D.EX = 0 330 | if val < 0 { 331 | D.EX = 0xFFFF 332 | } 333 | case 0x04: // MUL 334 | D.WaitState = 1 335 | a, b := uint32(I.GetOp(D, false)), uint32(I.GetOp(D, true)) 336 | val := b * a 337 | I.SetOp(D, (uint16)(val&0xFFFF)) 338 | D.EX = uint16((val >> 16) & 0xFFFF) 339 | case 0x05: // MLI 340 | D.WaitState = 1 341 | a, b := int32(int16(I.GetOp(D, false))), int32(int16(I.GetOp(D, true))) 342 | val := b * a 343 | I.SetOp(D, (uint16)(val&0xFFFF)) 344 | D.EX = uint16((val >> 16) & 0xFFFF) 345 | case 0x06: // DIV 346 | D.WaitState = 2 347 | a, b := uint32(I.GetOp(D, false)), uint32(I.GetOp(D, true)) 348 | if a == 0 { 349 | I.SetOp(D, 0) 350 | D.EX = 0 351 | } else { 352 | val := (b << 16) / a 353 | I.SetOp(D, (uint16)((val>>16)&0xFFFF)) 354 | D.EX = (uint16)(val & 0xFFFF) 355 | } 356 | case 0x07: // DVI 357 | D.WaitState = 2 358 | a, b := int32(int16(I.GetOp(D, false))), int32(int16(I.GetOp(D, true))) 359 | if a == 0 { 360 | I.SetOp(D, 0) 361 | D.EX = 0 362 | } else { 363 | sign := (a < 0) != (b < 0) 364 | if a < 0 { 365 | a = -a 366 | } 367 | if b < 0 { 368 | b = -b 369 | } 370 | val := (b << 16) / a 371 | if sign { 372 | val = -val 373 | } 374 | I.SetOp(D, (uint16)((val>>16)&0xFFFF)) 375 | D.EX = (uint16)(val & 0xFFFF) 376 | } 377 | case 0x08: // MOD 378 | D.WaitState = 2 379 | a, b := uint16(I.GetOp(D, false)), uint16(I.GetOp(D, true)) 380 | if a == 0 { 381 | b = 0 382 | } else { 383 | b = b % a 384 | } 385 | I.SetOp(D, uint16(b)) 386 | case 0x09: // MDI 387 | D.WaitState = 2 388 | a, b := int16(I.GetOp(D, false)), int16(I.GetOp(D, true)) 389 | if a == 0 { 390 | b = 0 391 | } else { 392 | b = b % a 393 | } 394 | I.SetOp(D, uint16(b)) 395 | case 0x0A: // AND 396 | val := I.GetOp(D, false) & I.GetOp(D, true) 397 | I.SetOp(D, val) 398 | case 0x0B: // BOR 399 | val := I.GetOp(D, false) | I.GetOp(D, true) 400 | I.SetOp(D, val) 401 | case 0x0C: // XOR 402 | val := I.GetOp(D, false) ^ I.GetOp(D, true) 403 | I.SetOp(D, val) 404 | case 0x0D: // SHR 405 | a, b := I.GetOp(D, false), I.GetOp(D, true) 406 | a = a & 31 407 | I.SetOp(D, b>>a) 408 | D.EX = uint16(((uint32(b) << 16) >> a) & 0xFFFF) 409 | case 0x0E: // ASR 410 | a, b := I.GetOp(D, false), int16(I.GetOp(D, true)) 411 | a = a & 31 412 | I.SetOp(D, uint16(b>>a)) 413 | D.EX = uint16((int32(uint32(b)<<16) >> a) & 0xFFFF) 414 | case 0x0F: // SHL 415 | a, b := I.GetOp(D, false), I.GetOp(D, true) 416 | a = a & 31 417 | I.SetOp(D, b<> 16) & 0xFFFF) 419 | case 0x10: // IFB 420 | D.WaitState = 1 421 | D.Skipping = (I.GetOp(D, false) & I.GetOp(D, true)) == 0 422 | case 0x11: // IFC 423 | D.WaitState = 1 424 | D.Skipping = (I.GetOp(D, false) & I.GetOp(D, true)) != 0 425 | case 0x12: // IFE 426 | D.WaitState = 1 427 | D.Skipping = I.GetOp(D, false) != I.GetOp(D, true) 428 | case 0x13: // IFN 429 | D.WaitState = 1 430 | D.Skipping = I.GetOp(D, false) == I.GetOp(D, true) 431 | case 0x14: // IFG 432 | D.WaitState = 1 433 | D.Skipping = I.GetOp(D, false) >= I.GetOp(D, true) 434 | case 0x15: // IFA 435 | D.WaitState = 1 436 | D.Skipping = int16(I.GetOp(D, false)) >= int16(I.GetOp(D, true)) 437 | case 0x16: // IFL 438 | D.WaitState = 1 439 | D.Skipping = I.GetOp(D, false) <= I.GetOp(D, true) 440 | case 0x17: // IFU 441 | D.WaitState = 1 442 | D.Skipping = int16(I.GetOp(D, false)) <= int16(I.GetOp(D, true)) 443 | case 0x1a: // ADX 444 | D.WaitState = 2 445 | a, b := uint32(I.GetOp(D, false)), uint32(I.GetOp(D, true)) 446 | val := a + b + uint32(D.EX) 447 | I.SetOp(D, uint16(val&0xFFFF)) 448 | D.EX = 0 449 | if val > 0xFFFF { 450 | D.EX = 1 451 | } 452 | case 0x1b: // SBX 453 | D.WaitState = 2 454 | a, b := uint32(I.GetOp(D, false)), uint32(I.GetOp(D, true)) 455 | val := b - a + uint32(D.EX) 456 | I.SetOp(D, uint16(val)) 457 | D.EX = 0 458 | if val < 0 { 459 | D.EX = 0xFFFF 460 | } 461 | case 0x1E: // STI 462 | D.WaitState = 1 463 | I.SetOp(D, I.GetOp(D, false)) 464 | D.Reg[6]++ 465 | D.Reg[7]++ 466 | case 0x1F: // STD 467 | D.WaitState = 1 468 | I.SetOp(D, I.GetOp(D, false)) 469 | D.Reg[6]-- 470 | D.Reg[7]-- 471 | case 0x00: // SPECIAL OPCODES 472 | switch I.OpB { 473 | case 0x01: // JSR 474 | D.WaitState = 2 475 | dest := I.GetOp(D, false) 476 | D.SP-- 477 | D.Mem.WriteMem(D.SP, D.PC) 478 | D.PC = dest 479 | case 0x08: // INT 480 | D.WaitState = 3 481 | D.Int(I.GetOp(D, false)) 482 | case 0x09: // IAG 483 | orig := I.OpB 484 | I.OpB = I.OpA 485 | I.AddB = I.AddA 486 | I.SetOp(D, D.IA) 487 | I.OpB = orig 488 | case 0x0A: // IAS 489 | D.WaitState = 2 490 | D.IA = I.GetOp(D, false) 491 | case 0x0B: // RFI 492 | D.WaitState = 1 493 | D.EnIQ = true 494 | D.Reg[0] = D.Mem.ReadMem(D.SP) 495 | D.PC = D.Mem.ReadMem(D.SP + 1) 496 | D.SP += 2 497 | case 0x0C: // IAQ 498 | D.EnIQ = I.GetOp(D, false) == 0 499 | case 0x10: // HWN 500 | D.WaitState = 1 501 | orig := I.OpB 502 | I.OpB = I.OpA 503 | I.AddB = I.AddA 504 | I.SetOp(D, uint16(len(D.Down))) 505 | I.OpB = orig 506 | case 0x11: // HWQ 507 | D.WaitState = 3 508 | id := int(I.GetOp(D, false)) 509 | D.Reg[0] = 0 510 | D.Reg[1] = 0 511 | D.Reg[2] = 0 512 | D.Reg[3] = 0 513 | D.Reg[4] = 0 514 | if id < len(D.Down) && D.Down[id] != nil { 515 | D.Down[id].HWQ(D) 516 | } 517 | case 0x12: // HWI 518 | D.WaitState = 3 519 | id := int(I.GetOp(D, false)) 520 | if id < len(D.Down) && D.Down[id] != nil { 521 | D.Down[id].HWI(D) 522 | } 523 | case 0x13: // LOG 524 | //log.Printf("DCPU Log: %04x\n", I.GetOp(D, false)) 525 | case 0x14: // BRK 526 | case 0x15: // HLT 527 | D.WaitInt = true 528 | default: 529 | //log.Printf("DCPU: Invalid opcode: %04x\n", I.Opcode) 530 | D.Running = false 531 | } 532 | default: 533 | //log.Printf("DCPU: Invalid opcode: %04x\n", I.Opcode) 534 | D.Running = false 535 | } 536 | } 537 | 538 | type DCPU struct { 539 | Hardware 540 | Reg [8]uint16 541 | PC uint16 542 | SP uint16 543 | EX uint16 544 | IA uint16 545 | IQLen uint16 546 | IQ [256]uint16 547 | EnIQ bool 548 | WaitState int 549 | Skipping bool 550 | Running bool 551 | WaitInt bool 552 | TickRate int 553 | SpareTicks int 554 | 555 | Mem *Mem16x64k 556 | } 557 | 558 | var dcpuClass = &HardwareClass{ 559 | Name: "dcpu", 560 | Desc: "DCPU-16 1.7", 561 | } 562 | 563 | func init() { 564 | RegisterClass(dcpuClass) 565 | } 566 | 567 | func NewDCPU(cycleRate int) *DCPU { 568 | if cycleRate == 0 { 569 | cycleRate = 100000 570 | } 571 | tickRate := 1 572 | if cycleRate > 100000 { 573 | //log.Printf("Error: cycle rate above 100Khz requested!\n") 574 | } else { 575 | tickRate = int(100000 / cycleRate) 576 | } 577 | 578 | dcpu := &DCPU{TickRate: tickRate, Mem: &Mem16x64k{}} 579 | 580 | dcpu.Mem.RawRAM = []byte{} 581 | bytesHeader := (*reflect.SliceHeader)(unsafe.Pointer(&dcpu.Mem.RawRAM)) 582 | bytesHeader.Data = uintptr(unsafe.Pointer(&dcpu.Mem.RAM[0])) 583 | bytesHeader.Len = 65536 * 2 584 | bytesHeader.Cap = 65536 * 2 585 | 586 | dcpu.Class = dcpuClass 587 | 588 | return dcpu 589 | } 590 | 591 | func (D *DCPU) String() string { 592 | return fmt.Sprintf("A: %04x B: %04x C: %04x X: %04x Y: %04x Z: %04x I: %04x J: %04x PC: %04x SP: %04x EX: %04x IA: %04x", 593 | D.Reg[0], D.Reg[1], D.Reg[2], D.Reg[3], D.Reg[4], D.Reg[5], D.Reg[6], D.Reg[7], 594 | D.PC, D.SP, D.EX, D.IA) 595 | } 596 | 597 | func (D *DCPU) Tick(ticks int) { 598 | if !D.Running { 599 | return 600 | } 601 | ticks, D.SpareTicks = (ticks+D.SpareTicks)/D.TickRate, (ticks+D.SpareTicks)%D.TickRate 602 | 603 | for l1 := 0; l1 < ticks; l1++ { 604 | //DCPUTick++ 605 | if D.WaitState > 0 { 606 | D.WaitState-- 607 | continue 608 | } 609 | if !D.Skipping && D.EnIQ && D.IQLen > 0 { 610 | D.IQLen-- 611 | D.EnIQ = false 612 | D.Mem.WriteMem(D.SP-1, D.PC) 613 | D.Mem.WriteMem(D.SP-2, D.Reg[0]) 614 | D.SP -= 2 615 | D.PC = D.IA 616 | D.Reg[0] = D.IQ[0] 617 | copy(D.IQ[:], D.IQ[1:]) 618 | D.WaitInt = false 619 | } 620 | if D.WaitInt || !D.Running { 621 | return 622 | } 623 | 624 | I := Decode(D.Mem.ReadMem(D.PC)) 625 | 626 | D.PC++ 627 | I.Run(D) 628 | 629 | } 630 | } 631 | 632 | func (D *DCPU) Start() { 633 | D.Reset() 634 | D.Hardware.Start() 635 | D.Running = true 636 | } 637 | 638 | func (D *DCPU) Stop() { 639 | D.Running = false 640 | D.Hardware.Stop() 641 | } 642 | 643 | func (D *DCPU) Reset() { 644 | for i := range D.Reg { 645 | D.Reg[i] = 0 646 | } 647 | D.PC = 0 648 | D.IA = 0 649 | D.EX = 0 650 | D.SP = 0 651 | D.IQLen = 0 652 | D.EnIQ = true 653 | if D.Mem != nil { 654 | D.Mem.Reset() 655 | } 656 | D.Hardware.Reset() 657 | } 658 | 659 | func (D *DCPU) Int(msg uint16) { 660 | if D.IA == 0 { 661 | return 662 | } 663 | if D.IQLen >= 256 { 664 | return 665 | } 666 | D.IQ[D.IQLen] = msg 667 | D.IQLen++ 668 | } 669 | 670 | func (D *DCPU) GetMem() IMem { 671 | return D.Mem 672 | } 673 | 674 | type Mem16x64k struct { 675 | RAM [65536]uint16 676 | RawRAM []byte 677 | Dirty [4096]uint8 678 | SyncCount [4096]uint8 679 | } 680 | 681 | type Sync struct { 682 | addr uint16 683 | synclen uint16 684 | registered bool 685 | mem *Mem16x64k 686 | } 687 | 688 | func (S *Sync) Register(mem *Mem16x64k) { 689 | if !S.registered { 690 | for l1 := S.addr >> 4; l1 <= ((S.addr + S.synclen) >> 4); l1++ { 691 | mem.SyncCount[l1]++ 692 | mem.Dirty[l1] = 1 693 | } 694 | S.registered = true 695 | S.mem = mem 696 | } 697 | } 698 | 699 | func (S *Sync) Unregister() { 700 | if S.registered { 701 | for l1 := S.addr >> 4; l1 <= ((S.addr + S.synclen) >> 4); l1++ { 702 | S.mem.SyncCount[l1]-- 703 | } 704 | S.registered = false 705 | S.mem = nil 706 | } 707 | } 708 | 709 | func (M *Mem16x64k) ReadMem(addr uint16) uint16 { 710 | if int(addr) > cap(M.RAM) { 711 | //log.Fatal(errors.New("Out of bounds memory ReadMem")) 712 | } 713 | return M.RAM[addr] 714 | } 715 | 716 | func (M *Mem16x64k) WriteMem(addr uint16, val uint16) { 717 | if int(addr) > cap(M.RAM) { 718 | //log.Fatal(errors.New("Out of bounds memory WriteMem")) 719 | } 720 | M.Dirty[addr>>4] = 1 721 | M.RAM[addr] = val 722 | } 723 | 724 | func (M *Mem16x64k) LoadMem(data []uint16) { 725 | copy(M.RAM[:], data) 726 | } 727 | 728 | func (M *Mem16x64k) GetRaw() []uint16 { 729 | return M.RAM[:] 730 | } 731 | 732 | func (M *Mem16x64k) RegisterSync(addr uint16, synclen uint16) *Sync { 733 | ret := &Sync{ 734 | addr: addr, 735 | synclen: synclen, 736 | } 737 | ret.Register(M) 738 | return ret 739 | } 740 | 741 | func (M *Mem16x64k) Reset() { 742 | for i := range M.RAM { 743 | M.RAM[i] = 0 744 | } 745 | } 746 | -------------------------------------------------------------------------------- /hardware_eeprom.go: -------------------------------------------------------------------------------- 1 | package gemu 2 | 3 | type EEPROM struct { 4 | Data []uint16 5 | } 6 | 7 | func (E *EEPROM) HWI(D *DCPU) { 8 | switch D.Reg[1] { 9 | case 1: 10 | D.Reg[4] = E.Data[D.Reg[3]] 11 | D.WaitState += 1000 / D.TickRate 12 | case 2: 13 | E.Data[D.Reg[3]] &= D.Reg[4] 14 | D.WaitState += 5000 / D.TickRate 15 | case 3: 16 | for i := range E.Data { 17 | E.Data[i] = 0xFFFF 18 | } 19 | D.WaitState += 10000 / D.TickRate 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /hardware_floppy.go: -------------------------------------------------------------------------------- 1 | package gemu 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | var floppyClass = &HardwareClass{ 9 | Name: "mack_35fd", 10 | Desc: "Mackapar M35FD", 11 | DevID: 0x4fd524c5, 12 | VerID: 0x000b, 13 | MfgID: 0x1eb37e91, 14 | } 15 | 16 | func init() { 17 | RegisterClass(floppyClass) 18 | } 19 | 20 | type M35FD struct { 21 | Hardware 22 | Error uint16 23 | interrupt uint16 24 | 25 | Running bool 26 | TicksLeft int 27 | Block []byte 28 | Addr uint16 29 | Read bool 30 | ActionDone bool 31 | 32 | Disk string 33 | 34 | NeedSync bool 35 | 36 | storage Storage 37 | } 38 | 39 | func NewM35FD(flip bool) *M35FD { 40 | floppy := &M35FD{} 41 | floppy.Class = floppyClass 42 | floppy.NeedSync = true 43 | floppy.storage = defaultStorage 44 | if flip { 45 | floppy.storage = NewFlipStorage(defaultStorage) 46 | } 47 | return floppy 48 | } 49 | 50 | func (fd *M35FD) HWI(D *DCPU) { 51 | switch D.Reg[0] { 52 | case 0: 53 | if fd.Running { 54 | D.Reg[1] = 3 55 | } else if fd.Disk != "" { 56 | /*if disk.Readonly { 57 | D.Reg[1] = 2 58 | } else { 59 | D.Reg[1] = 1 60 | }*/ 61 | D.Reg[1] = 1 // Need to reenable readonly checks 62 | } else { 63 | D.Reg[1] = 0 64 | } 65 | D.Reg[2] = fd.Error 66 | fd.Error = 0 67 | case 1: 68 | fd.interrupt = D.Reg[3] 69 | case 2: 70 | if fd.Disk != "" { 71 | fd.Block = make([]byte, 1024) 72 | fd.ActionDone = false 73 | go func(offset int) { fd.storage.Read(fd.Disk, offset, fd.Block); fd.ActionDone = true }(int(D.Reg[3]) * 1024) 74 | fd.Running = true 75 | fd.TicksLeft = 10000 76 | fd.Addr = D.Reg[4] 77 | fd.Read = true 78 | fd.NeedSync = true 79 | D.Reg[1] = 1 80 | } else { 81 | fd.Error = 2 82 | D.Reg[1] = 0 83 | } 84 | if fd.interrupt != 0 && fd.Up != nil { 85 | if dcpu, ok := fd.Up.(*DCPU); ok { 86 | dcpu.Int(fd.interrupt) 87 | } 88 | } 89 | case 3: 90 | if fd.Disk != "" { 91 | 92 | fd.Block = make([]byte, 1024) 93 | rawData := []uint16{} 94 | bytesHeader := (*reflect.SliceHeader)(unsafe.Pointer(&rawData)) 95 | bytesHeader.Data = uintptr(unsafe.Pointer(&fd.Block[0])) 96 | bytesHeader.Len = 512 97 | bytesHeader.Cap = 512 98 | baseRam := fd.GetMem().GetRaw()[fd.Addr:] 99 | copy(rawData, baseRam) 100 | go func(offset int) { fd.storage.Write(fd.Disk, offset, fd.Block); fd.ActionDone = true }(int(D.Reg[3]) * 1024) 101 | fd.Running = true 102 | fd.TicksLeft = 10000 103 | fd.Addr = D.Reg[4] 104 | fd.Read = false 105 | fd.NeedSync = true 106 | D.Reg[1] = 1 107 | } else { 108 | fd.Error = 2 109 | D.Reg[1] = 0 110 | } 111 | if fd.interrupt != 0 && fd.Up != nil { 112 | if dcpu, ok := fd.Up.(*DCPU); ok { 113 | dcpu.Int(fd.interrupt) 114 | } 115 | } 116 | } 117 | } 118 | 119 | func (fd *M35FD) Tick(ticks int) { 120 | if fd.Running { 121 | fd.TicksLeft -= ticks 122 | if fd.TicksLeft <= 0 { 123 | if fd.Read { 124 | if fd.ActionDone { 125 | rawData := []uint16{} 126 | bytesHeader := (*reflect.SliceHeader)(unsafe.Pointer(&rawData)) 127 | bytesHeader.Data = uintptr(unsafe.Pointer(&fd.Block[0])) 128 | bytesHeader.Len = 512 129 | bytesHeader.Cap = 512 130 | if fd.GetMem() != nil { 131 | copy(fd.GetMem().GetRaw()[fd.Addr:], rawData) 132 | } 133 | fd.Running = false 134 | fd.NeedSync = true 135 | if fd.interrupt != 0 && fd.Up != nil { 136 | if dcpu, ok := fd.Up.(*DCPU); ok { 137 | dcpu.Int(fd.interrupt) 138 | } 139 | } 140 | } 141 | } else { 142 | if fd.ActionDone { 143 | fd.Running = false 144 | fd.NeedSync = true 145 | if fd.interrupt != 0 && fd.Up != nil { 146 | if dcpu, ok := fd.Up.(*DCPU); ok { 147 | dcpu.Int(fd.interrupt) 148 | } 149 | } 150 | } 151 | } 152 | 153 | } 154 | } 155 | } 156 | 157 | func (fd *M35FD) Reset() { 158 | fd.Error = 0 159 | } 160 | 161 | func (fd *M35FD) ChangeDisk(disk string) { 162 | fd.Disk = disk 163 | if fd.interrupt != 0 && fd.Up != nil { 164 | if dcpu, ok := fd.Up.(*DCPU); ok { 165 | dcpu.Int(fd.interrupt) 166 | } 167 | } 168 | } 169 | 170 | func (fd *M35FD) IsDirty() bool { 171 | return fd.NeedSync 172 | } 173 | 174 | func (fd *M35FD) ClearDirty() { 175 | fd.NeedSync = false 176 | } 177 | -------------------------------------------------------------------------------- /hardware_keyboard.go: -------------------------------------------------------------------------------- 1 | package gemu 2 | 3 | const ( 4 | CLEAR_BUFFER uint16 = 0 5 | GET_NEXT = 1 6 | CHECK_KEY = 2 7 | SET_INT = 3 8 | SET_MODE = 4 9 | ) 10 | 11 | var keyboardClass = &HardwareClass{ 12 | Name: "keyboard", 13 | Desc: "Generic Keyboard", 14 | DevID: 0x30c17406, 15 | VerID: 0x0001, 16 | MfgID: 0x1c6c8b36, 17 | } 18 | 19 | func init() { 20 | RegisterClass(keyboardClass) 21 | } 22 | 23 | type Keyboard struct { 24 | Hardware 25 | keycount int 26 | keybuffer [8]uint8 27 | keydown [8]uint8 28 | interrupt uint16 29 | mode uint16 30 | } 31 | 32 | func NewKeyboard() *Keyboard { 33 | keyboard := &Keyboard{} 34 | keyboard.Class = keyboardClass 35 | return keyboard 36 | } 37 | 38 | func (K *Keyboard) HWI(D *DCPU) { 39 | switch D.Reg[0] { 40 | case CLEAR_BUFFER: 41 | K.keycount = 0 42 | case GET_NEXT: 43 | if K.keycount > 0 { 44 | D.Reg[2] = uint16(K.keybuffer[0]) 45 | for i := 0; i < 7; i++ { 46 | K.keybuffer[i] = K.keybuffer[i+1] 47 | } 48 | K.keycount-- 49 | } else { 50 | D.Reg[2] = 0 51 | } 52 | case CHECK_KEY: 53 | D.Reg[2] = 0 54 | for i := 0; i < 7; i++ { 55 | if K.keydown[i] == uint8(D.Reg[1]) { 56 | D.Reg[2] = 1 57 | } 58 | } 59 | case SET_INT: 60 | K.interrupt = D.Reg[1] 61 | case SET_MODE: 62 | K.mode = D.Reg[1] 63 | K.keycount = 0 64 | for i := range K.keydown { 65 | K.keydown[i] = 0 66 | } 67 | } 68 | } 69 | 70 | func (K *Keyboard) RawKey(key uint16, state bool) { 71 | if key >= 0x80 || K.mode == 1 { 72 | if state { 73 | handled := false 74 | for i := 0; i < 8; i++ { 75 | if K.keydown[i] == 0 { 76 | K.keydown[i] = uint8(key) 77 | handled = true 78 | break 79 | } 80 | } 81 | if !handled { 82 | K.keydown[7] = uint8(key) 83 | } 84 | } else { 85 | for i := 0; i < 8; i++ { 86 | if K.keydown[i] == uint8(key) { 87 | K.keydown[i] = 0 88 | } 89 | } 90 | } 91 | if !state { 92 | key |= 0x8000 93 | } 94 | if state || K.mode == 1 { 95 | K.queueKey(key) 96 | } 97 | } 98 | } 99 | 100 | func (K *Keyboard) ParsedKey(key uint16) { 101 | if K.mode == 0 { 102 | K.queueKey(key) 103 | } 104 | } 105 | 106 | func (K *Keyboard) queueKey(key uint16) { 107 | if K.keycount < 8 { 108 | K.keybuffer[K.keycount] = uint8(key) 109 | K.keycount++ 110 | } else { 111 | copy(K.keybuffer[:], K.keybuffer[1:]) 112 | K.keybuffer[7] = uint8(key) 113 | } 114 | if K.interrupt != 0 { 115 | if K.Up != nil { 116 | if dcpu, ok := K.Up.(*DCPU); ok { 117 | dcpu.Int(K.interrupt) 118 | } 119 | } 120 | } 121 | } 122 | 123 | func (K *Keyboard) Reset() { 124 | K.keycount = 0 125 | for i := range K.keydown { 126 | K.keydown[i] = 0 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /hardware_lem.go: -------------------------------------------------------------------------------- 1 | package gemu 2 | 3 | var LemDefFont = []uint16{ 4 | 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 5 | 0x242e, 0x2400, 0x082A, 0x0800, 0x0008, 0x0000, 0x0808, 0x0808, 6 | 0x00ff, 0x0000, 0x00f8, 0x0808, 0x08f8, 0x0000, 0x080f, 0x0000, 7 | 0x000f, 0x0808, 0x00ff, 0x0808, 0x08f8, 0x0808, 0x08ff, 0x0000, 8 | 0x080f, 0x0808, 0x08ff, 0x0808, 0x6633, 0x99cc, 0x9933, 0x66cc, 9 | 0xfef8, 0xe080, 0x7f1f, 0x0701, 0x0107, 0x1f7f, 0x80e0, 0xf8fe, 10 | 0x5500, 0xAA00, 0x55AA, 0x55AA, 0xffAA, 0xff55, 0x0f0f, 0x0f0f, 11 | 0xf0f0, 0xf0f0, 0x0000, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 12 | 0x0000, 0x0000, 0x005f, 0x0000, 0x0300, 0x0300, 0x3e14, 0x3e00, 13 | 0x266b, 0x3200, 0x611c, 0x4300, 0x3629, 0x7650, 0x0002, 0x0100, 14 | 0x1c22, 0x4100, 0x4122, 0x1c00, 0x1408, 0x1400, 0x081C, 0x0800, 15 | 0x4020, 0x0000, 0x0808, 0x0800, 0x0040, 0x0000, 0x601c, 0x0300, 16 | 0x3e49, 0x3e00, 0x427f, 0x4000, 0x6259, 0x4600, 0x2249, 0x3600, 17 | 0x0f08, 0x7f00, 0x2745, 0x3900, 0x3e49, 0x3200, 0x6119, 0x0700, 18 | 0x3649, 0x3600, 0x2649, 0x3e00, 0x0024, 0x0000, 0x4024, 0x0000, 19 | 0x0814, 0x2241, 0x1414, 0x1400, 0x4122, 0x1408, 0x0259, 0x0600, 20 | 0x3e59, 0x5e00, 0x7e09, 0x7e00, 0x7f49, 0x3600, 0x3e41, 0x2200, 21 | 0x7f41, 0x3e00, 0x7f49, 0x4100, 0x7f09, 0x0100, 0x3e41, 0x7a00, 22 | 0x7f08, 0x7f00, 0x417f, 0x4100, 0x2040, 0x3f00, 0x7f08, 0x7700, 23 | 0x7f40, 0x4000, 0x7f06, 0x7f00, 0x7f01, 0x7e00, 0x3e41, 0x3e00, 24 | 0x7f09, 0x0600, 0x3e41, 0xbe00, 0x7f09, 0x7600, 0x2649, 0x3200, 25 | 0x017f, 0x0100, 0x3f40, 0x3f00, 0x1f60, 0x1f00, 0x7f30, 0x7f00, 26 | 0x7708, 0x7700, 0x0778, 0x0700, 0x7149, 0x4700, 0x007f, 0x4100, 27 | 0x031c, 0x6000, 0x0041, 0x7f00, 0x0201, 0x0200, 0x8080, 0x8000, 28 | 0x0001, 0x0200, 0x2454, 0x7800, 0x7f44, 0x3800, 0x3844, 0x2800, 29 | 0x3844, 0x7f00, 0x3854, 0x5800, 0x087e, 0x0900, 0x4854, 0x3c00, 30 | 0x7f04, 0x7800, 0x447d, 0x4000, 0x2040, 0x3d00, 0x7f10, 0x6c00, 31 | 0x417f, 0x4000, 0x7c18, 0x7c00, 0x7c04, 0x7800, 0x3844, 0x3800, 32 | 0x7c14, 0x0800, 0x0814, 0x7c00, 0x7c04, 0x0800, 0x4854, 0x2400, 33 | 0x043e, 0x4400, 0x3c40, 0x7c00, 0x1c60, 0x1c00, 0x7c30, 0x7c00, 34 | 0x6c10, 0x6c00, 0x4c50, 0x3c00, 0x6454, 0x4c00, 0x0836, 0x4100, 35 | 0x0077, 0x0000, 0x4136, 0x0800, 0x0201, 0x0201, 0x0205, 0x0200} 36 | var LemDefPal = []uint16{ 37 | 0x0000, 0x000a, 0x00a0, 0x00aa, 38 | 0x0a00, 0x0a0a, 0x0a50, 0x0aaa, 39 | 0x0555, 0x055f, 0x05f5, 0x05ff, 40 | 0x0f55, 0x0f5f, 0x0ff5, 0x0fff} 41 | 42 | var lemClass = &HardwareClass{ 43 | Name: "nya_lem", 44 | Desc: "Nya LEM 1802", 45 | DevID: 0x734df615, 46 | VerID: 0x1802, 47 | MfgID: 0x1c6c8b36, 48 | } 49 | 50 | func init() { 51 | RegisterClass(lemClass) 52 | } 53 | 54 | func NewLem1802() *Lem1802 { 55 | lem := &Lem1802{} 56 | lem.Class = lemClass 57 | lem.NeedSync = true 58 | return lem 59 | } 60 | 61 | func (L *Lem1802) HWI(D *DCPU) { 62 | switch D.Reg[0] { 63 | case 0: 64 | if L.dspSync != nil { 65 | L.dspSync.Unregister() 66 | L.dspSync = nil 67 | } 68 | L.DspMem = D.Reg[1] 69 | if L.DspMem != 0 { 70 | L.dspSync = D.Mem.RegisterSync(D.Reg[1], 384) 71 | } 72 | L.NeedSync = true 73 | case 1: 74 | if L.fontSync != nil { 75 | L.fontSync.Unregister() 76 | L.fontSync = nil 77 | } 78 | L.FontMem = D.Reg[1] 79 | if L.FontMem != 0 { 80 | L.fontSync = D.Mem.RegisterSync(D.Reg[1], 256) 81 | } 82 | L.NeedSync = true 83 | case 2: 84 | if L.palSync != nil { 85 | L.palSync.Unregister() 86 | L.palSync = nil 87 | } 88 | L.PalMem = D.Reg[1] 89 | if L.PalMem != 0 { 90 | L.palSync = D.Mem.RegisterSync(D.Reg[1], 16) 91 | } 92 | L.NeedSync = true 93 | case 3: 94 | L.Border = D.Reg[1] 95 | L.NeedSync = true 96 | case 4: 97 | copy(L.GetMem().GetRaw()[D.Reg[1]:], LemDefFont) 98 | case 5: 99 | copy(L.GetMem().GetRaw()[D.Reg[1]:], LemDefPal) 100 | } 101 | } 102 | 103 | type Lem1802 struct { 104 | Hardware 105 | NeedSync bool 106 | 107 | DspMem uint16 108 | FontMem uint16 109 | PalMem uint16 110 | Border uint16 111 | 112 | dspSync *Sync 113 | fontSync *Sync 114 | palSync *Sync 115 | } 116 | 117 | func (L *Lem1802) Reset() { 118 | L.DspMem = 0 119 | L.FontMem = 0 120 | L.PalMem = 0 121 | L.Border = 0 122 | if L.dspSync != nil { 123 | L.dspSync.Unregister() 124 | } 125 | L.dspSync = nil 126 | if L.fontSync != nil { 127 | L.fontSync.Unregister() 128 | } 129 | L.fontSync = nil 130 | if L.palSync != nil { 131 | L.palSync.Unregister() 132 | } 133 | L.palSync = nil 134 | L.NeedSync = true 135 | } 136 | 137 | func (L *Lem1802) IsDirty() bool { 138 | return L.NeedSync 139 | } 140 | 141 | func (L *Lem1802) ClearDirty() { 142 | L.NeedSync = false 143 | } 144 | -------------------------------------------------------------------------------- /hardware_pixie.go: -------------------------------------------------------------------------------- 1 | package gemu 2 | 3 | var pixieClass = &HardwareClass{ 4 | Name: "pixie", 5 | Desc: "PIXIE", 6 | DevID: 0x774df615, 7 | VerID: 0x1802, 8 | MfgID: 0x83610EC5, 9 | } 10 | 11 | func init() { 12 | RegisterClass(pixieClass) 13 | } 14 | 15 | func NewPIXIE() *PIXIE { 16 | pixie := &PIXIE{} 17 | pixie.Class = pixieClass 18 | pixie.NeedSync = true 19 | return pixie 20 | } 21 | 22 | func (P *PIXIE) HWI(D *DCPU) { 23 | switch D.Reg[0] { 24 | case 0: 25 | if P.dspSync != nil { 26 | P.dspSync.Unregister() 27 | P.dspSync = nil 28 | } 29 | P.DspMem = D.Reg[1] 30 | if P.DspMem != 0 { 31 | syncCount := uint16(384) 32 | if P.Mode >= 1 && P.Mode <= 4 { 33 | syncCount = P.Mode * 768 34 | } 35 | P.dspSync = D.Mem.RegisterSync(D.Reg[1], syncCount) 36 | } 37 | P.NeedSync = true 38 | case 1: 39 | if P.fontSync != nil { 40 | P.fontSync.Unregister() 41 | P.fontSync = nil 42 | } 43 | P.FontMem = D.Reg[1] 44 | if P.FontMem != 0 { 45 | P.fontSync = D.Mem.RegisterSync(D.Reg[1], 256) 46 | } 47 | P.NeedSync = true 48 | case 2: 49 | if P.palSync != nil { 50 | P.palSync.Unregister() 51 | P.palSync = nil 52 | } 53 | P.PalMem = D.Reg[1] 54 | if P.PalMem != 0 { 55 | P.palSync = D.Mem.RegisterSync(D.Reg[1], 16) 56 | } 57 | P.NeedSync = true 58 | case 3: 59 | P.Border = D.Reg[1] 60 | P.NeedSync = true 61 | case 4: 62 | copy(P.GetMem().GetRaw()[D.Reg[1]:], LemDefFont) 63 | case 5: 64 | copy(P.GetMem().GetRaw()[D.Reg[1]:], LemDefPal) 65 | case 16: 66 | P.Mode = D.Reg[1] 67 | if P.DspMem != 0 { 68 | P.dspSync.Unregister() 69 | syncCount := uint16(384) 70 | if P.Mode >= 1 && P.Mode <= 4 { 71 | syncCount = P.Mode * 768 72 | } 73 | P.dspSync = D.Mem.RegisterSync(P.DspMem, syncCount) 74 | } 75 | if P.Mode > 0 { 76 | if P.fontSync != nil { 77 | P.fontSync.Unregister() 78 | P.fontSync = nil 79 | } 80 | } else { 81 | if P.fontSync == nil && P.FontMem != 0 { 82 | P.fontSync = D.Mem.RegisterSync(P.FontMem, 256) 83 | } 84 | } 85 | } 86 | } 87 | 88 | type PIXIE struct { 89 | Hardware 90 | NeedSync bool 91 | 92 | DspMem uint16 93 | FontMem uint16 94 | PalMem uint16 95 | Border uint16 96 | Mode uint16 97 | LEMCompat bool 98 | 99 | dspSync *Sync 100 | fontSync *Sync 101 | palSync *Sync 102 | } 103 | 104 | func (P *PIXIE) Reset() { 105 | P.DspMem = 0 106 | P.FontMem = 0 107 | P.PalMem = 0 108 | P.Border = 0 109 | P.Mode = 0 110 | if P.dspSync != nil { 111 | P.dspSync.Unregister() 112 | } 113 | P.dspSync = nil 114 | if P.fontSync != nil { 115 | P.fontSync.Unregister() 116 | } 117 | P.fontSync = nil 118 | if P.palSync != nil { 119 | P.palSync.Unregister() 120 | } 121 | P.palSync = nil 122 | P.NeedSync = true 123 | } 124 | 125 | func (P *PIXIE) IsDirty() bool { 126 | return P.NeedSync 127 | } 128 | 129 | func (P *PIXIE) ClearDirty() { 130 | P.NeedSync = false 131 | } 132 | 133 | func (P *PIXIE) SetLEMCompat(lemCompat bool) { 134 | P.LEMCompat = lemCompat 135 | } 136 | 137 | func (P *PIXIE) HWQ(D *DCPU) { 138 | M := pixieClass 139 | C := pixieClass 140 | if P.LEMCompat { 141 | C = lemClass 142 | } 143 | D.Reg[0] = uint16(C.DevID & 0xFFFF) 144 | D.Reg[1] = uint16((C.DevID >> 16) & 0xFFFF) 145 | D.Reg[2] = C.VerID 146 | D.Reg[3] = uint16(M.MfgID & 0xFFFF) 147 | D.Reg[4] = uint16((M.MfgID >> 16) & 0xFFFF) 148 | } 149 | -------------------------------------------------------------------------------- /hardware_rom.go: -------------------------------------------------------------------------------- 1 | package gemu 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | var romClass = &HardwareClass{ 9 | Name: "rom", 10 | Desc: "Embedded ROM", 11 | DevID: 0x17400011, 12 | VerID: 0x0001, 13 | MfgID: 0x12452135, 14 | } 15 | 16 | func init() { 17 | RegisterClass(romClass) 18 | } 19 | 20 | type ROM struct { 21 | Hardware 22 | Data []uint16 23 | } 24 | 25 | func NewRom(romImage string, flip bool) *ROM { 26 | rom := &ROM{} 27 | rom.Class = romClass 28 | storage := defaultStorage 29 | if flip { 30 | storage = NewFlipStorage(storage) 31 | } 32 | if storage.Exists(romImage) { 33 | rom.Data = make([]uint16, storage.Length(romImage)/2) 34 | rawData := []byte{} 35 | bytesHeader := (*reflect.SliceHeader)(unsafe.Pointer(&rawData)) 36 | bytesHeader.Data = uintptr(unsafe.Pointer(&rom.Data[0])) 37 | bytesHeader.Len = len(rom.Data) * 2 38 | bytesHeader.Cap = len(rom.Data) * 2 39 | storage.Read(romImage, 0, rawData) 40 | } 41 | return rom 42 | } 43 | 44 | func (R *ROM) HWI(D *DCPU) { 45 | switch D.Reg[0] { 46 | case 0: 47 | D.Mem.LoadMem(R.Data) 48 | D.PC = 0 49 | case 1: 50 | copy(R.Data, D.Mem.GetRaw()[D.Reg[1]:]) 51 | } 52 | } 53 | 54 | func (R *ROM) Reset() { 55 | if R.Up != nil { 56 | if dcpu, ok := R.Up.(*DCPU); ok { 57 | dcpu.Mem.LoadMem(R.Data) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /storage.go: -------------------------------------------------------------------------------- 1 | package gemu 2 | 3 | var defaultStorage Storage 4 | 5 | func SetStorage(storage Storage) { 6 | defaultStorage = storage 7 | } 8 | 9 | type Storage interface { 10 | Exists(Item string) bool 11 | Length(Item string) int 12 | Read(Item string, offset int, data []byte) 13 | Write(Item string, offset int, data []byte) 14 | } 15 | -------------------------------------------------------------------------------- /storage_disk.go: -------------------------------------------------------------------------------- 1 | package gemu 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | type DiskStorage struct { 10 | basepath string 11 | } 12 | 13 | func NewDiskStorage(basepath string) Storage { 14 | return &DiskStorage{basepath: basepath} 15 | } 16 | 17 | func (DS *DiskStorage) Exists(Item string) bool { 18 | _, err := os.Stat(filepath.Join(DS.basepath, Item)) 19 | if err == nil { 20 | return true 21 | } 22 | return false 23 | } 24 | 25 | func (DS *DiskStorage) Length(Item string) int { 26 | s, err := os.Stat(filepath.Join(DS.basepath, Item)) 27 | if err != nil { 28 | return 0 29 | } 30 | return int(s.Size()) 31 | } 32 | 33 | func (DS *DiskStorage) Read(Item string, offset int, data []byte) { 34 | if DS.Exists(Item) { 35 | file, err := os.Open(filepath.Join(DS.basepath, Item)) 36 | if err != nil { 37 | return 38 | } 39 | defer file.Close() 40 | _, err = file.Seek(int64(offset), os.SEEK_SET) 41 | if err != nil { 42 | return 43 | } 44 | _, err = io.ReadFull(file, data) 45 | if err != nil { 46 | return 47 | } 48 | } else { 49 | } 50 | } 51 | 52 | func (DS *DiskStorage) Write(Item string, offset int, data []byte) { 53 | file, err := os.OpenFile(filepath.Join(DS.basepath, Item), os.O_CREATE | os.O_WRONLY, 0777) 54 | if err != nil { 55 | return 56 | } 57 | defer file.Close() 58 | file.Seek(int64(offset), os.SEEK_SET) 59 | file.Write(data) 60 | } 61 | -------------------------------------------------------------------------------- /storage_flip.go: -------------------------------------------------------------------------------- 1 | package gemu 2 | 3 | type FlipStorage struct { 4 | Storage 5 | } 6 | 7 | func NewFlipStorage(base Storage) Storage { 8 | return &FlipStorage{base} 9 | } 10 | 11 | func (FS *FlipStorage) Read(Item string, offset int, data []byte) { 12 | FS.Storage.Read(Item, offset, data) 13 | for l1 := 0; l1 < len(data)/2; l1++ { 14 | data[l1*2], data[l1*2+1] = data[l1*2+1], data[l1*2] 15 | } 16 | } 17 | 18 | func (FS *FlipStorage) Write(Item string, offset int, data []byte) { 19 | for l1 := 0; l1 < len(data)/2; l1++ { 20 | data[l1*2], data[l1*2+1] = data[l1*2+1], data[l1*2] 21 | } 22 | FS.Storage.Write(Item, offset, data) 23 | } 24 | -------------------------------------------------------------------------------- /storage_multi.go: -------------------------------------------------------------------------------- 1 | package gemu 2 | 3 | type MultiStorage struct { 4 | storage []Storage 5 | } 6 | 7 | func NewMultiStorage(instorage ...Storage) Storage { 8 | return &MultiStorage{storage: instorage} 9 | } 10 | 11 | func (MS *MultiStorage) Exists(Item string) bool { 12 | for _, S := range MS.storage { 13 | if S.Exists(Item) { 14 | return true 15 | } 16 | } 17 | return false 18 | } 19 | 20 | func (MS *MultiStorage) Length(Item string) int { 21 | for _, S := range MS.storage { 22 | if S.Exists(Item) { 23 | return S.Length(Item) 24 | } 25 | } 26 | return 0 27 | } 28 | 29 | func (MS *MultiStorage) Read(Item string, offset int, data []byte) { 30 | for _, S := range MS.storage { 31 | if S.Exists(Item) { 32 | S.Read(Item, offset, data) 33 | return 34 | } 35 | } 36 | } 37 | 38 | func (MS *MultiStorage) Write(Item string, offset int, data []byte) { 39 | for _, S := range MS.storage { 40 | if S.Exists(Item) { 41 | S.Write(Item, offset, data) 42 | return 43 | } 44 | } 45 | MS.storage[0].Write(Item, offset, data) 46 | } 47 | --------------------------------------------------------------------------------