├── .gitignore ├── LICENSE ├── README.md ├── appleii ├── appleii.go ├── display.go └── keyboard.go ├── buildos.sh ├── cmd ├── a2 │ └── main.go ├── asm │ └── main.go ├── link │ └── main.go ├── nm │ └── main.go └── objdump │ ├── main.go │ └── test.bin ├── cpu ├── cpu.go ├── cpu_test.go ├── logic.go ├── mem.go └── testdata │ └── 6502_functional_test.bin.gz ├── go.mod ├── go.sum ├── ins ├── 6502_instruction_set.html ├── gen.py ├── ins.go ├── mode_string.go ├── name_string.go ├── op_gen.go └── pagepolicy_string.go ├── lisa ├── compile.go ├── decode.go ├── doc │ ├── Lisa_Assembler_1A.txt │ ├── Lisa_Assembler_1B.txt │ ├── Lisa_Assembler_2A.txt │ ├── Lisa_Assembler_2B.txt │ └── Lisa_Assembler_3A.txt ├── expr.go ├── expr_test.go ├── link.go ├── mnemonic_defs.go ├── mnemonic_string.go ├── objfile.go ├── operator.go ├── parser.go ├── stype_string.go ├── termtype_string.go └── testdata │ ├── basic.s │ ├── hello_world.s │ ├── hex.s │ ├── labels.s │ └── simple_kbd.s └── zhuos ├── README.md ├── src ├── keyin.s ├── nmi.s ├── reset.s ├── symbols.s └── vectors.s └── zp ├── decode.go ├── encode.go ├── encode_test.go └── file.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | *.swp 8 | *.swo 9 | *.rom 10 | *.log 11 | z.dat 12 | 13 | # Test binary, built with `go test -c` 14 | *.test 15 | 16 | # Output of the go coverage tool, specifically when used with LiteIDE 17 | *.out 18 | 19 | # Dependency directories (remove the comment below to include it) 20 | # vendor/ 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Meng Zhuo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go6502 2 | Go emulator for MOS 6502 and Apple II 3 | 4 | # Build ZHUOS 5 | `./buildos.sh` 6 | 7 | # Run OS on cheap Apple II 8 | `go run cmd/a2/main.go -r z.dat` 9 | 10 | # Note 11 | This is a experimental project and for fun. 12 | -------------------------------------------------------------------------------- /appleii/appleii.go: -------------------------------------------------------------------------------- 1 | package appleii 2 | 3 | import ( 4 | "encoding/json" 5 | "go6502/cpu" 6 | "io" 7 | "log" 8 | "os" 9 | "time" 10 | 11 | "github.com/rivo/tview" 12 | ) 13 | 14 | type AppleII struct { 15 | cpu *cpu.CPU 16 | app *tview.Application 17 | in *tview.Table 18 | Mem *cpu.SimpleMem 19 | log *log.Logger 20 | pixelMap map[uint16]*tview.TableCell 21 | 22 | RefreshMs int `json:"refresh_ms"` 23 | Log string `json:"log"` 24 | ROM string `json:"rom"` 25 | } 26 | 27 | func New() *AppleII { 28 | return &AppleII{} 29 | } 30 | 31 | func (a *AppleII) Init(in io.Reader) (err error) { 32 | d := json.NewDecoder(in) 33 | err = d.Decode(a) 34 | 35 | a.Mem = &cpu.SimpleMem{} 36 | 37 | if a.RefreshMs <= 10 { 38 | a.RefreshMs = 1000 / 60 39 | } 40 | 41 | if a.Log != "" { 42 | fd, err := os.OpenFile(a.Log, os.O_CREATE|os.O_WRONLY|os.O_SYNC, 0700) 43 | if err != nil { 44 | return err 45 | } 46 | a.log = log.New(fd, "[CPU]", log.LstdFlags) 47 | } 48 | a.cpu = cpu.New(a.log) 49 | 50 | a.in = tview.NewTable() 51 | a.in.SetBorder(false) 52 | 53 | cell := tview.NewTableCell(" ") 54 | a.in.SetCell(23, 40, cell) 55 | 56 | for r := 0; r < 24; r++ { 57 | for c := 0; c < 40; c++ { 58 | cell = tview.NewTableCell(" ") 59 | cell.SetSelectable(false) 60 | a.in.SetCell(r, c, cell) 61 | } 62 | } 63 | a.initDisplay() 64 | 65 | return 66 | } 67 | 68 | func (a *AppleII) Run() { 69 | 70 | go func() { 71 | tick := time.NewTicker(time.Duration(a.RefreshMs) * time.Millisecond) 72 | for { 73 | <-tick.C 74 | //a.cpu.NMI <- true 75 | a.app.Draw() 76 | } 77 | }() 78 | 79 | app := tview.NewApplication() 80 | app.EnableMouse(false) 81 | 82 | a.app = app 83 | app.SetInputCapture(a.uiKeyPress) 84 | 85 | a.cpu.DurPerCycles = time.Millisecond 86 | a.cpu.ResetF(a) 87 | 88 | //a.fullScreen() 89 | 90 | go a.cpu.Run(a) 91 | if err := app.SetRoot(a.in, true).SetFocus(a.in).Run(); err != nil { 92 | log.Fatal(err) 93 | } 94 | } 95 | 96 | func (a *AppleII) fullScreen() { 97 | for _, ba := range baseAddress { 98 | for j := uint16(0); j < 40; j++ { 99 | a.WriteByte(ba+j, byte(j)+'0') 100 | } 101 | } 102 | } 103 | 104 | func (a *AppleII) ReadByte(pc uint16) (b uint8) { 105 | b = a.Mem.ReadByte(pc) 106 | switch pc { 107 | case ClearKeyStrobe: 108 | // reset keyboard 109 | a.cpu.IRQ &= keyIRQMask 110 | } 111 | if a.log != nil { 112 | a.log.Printf("RB 0x%04X -> %x", pc, b) 113 | } 114 | return 115 | } 116 | 117 | func (a *AppleII) ReadWord(pc uint16) (n uint16) { 118 | n = a.Mem.ReadWord(pc) 119 | if a.log != nil { 120 | a.log.Printf("RW 0x%04X -> %x", pc, n) 121 | } 122 | return 123 | } 124 | 125 | func (a *AppleII) WriteByte(pc uint16, b uint8) { 126 | 127 | if pc >= 0x400 && pc <= 0x7ff { 128 | // must be printable 129 | if cell := a.pixelMap[pc]; cell != nil { 130 | cell.SetText(string(b)) 131 | } 132 | } 133 | 134 | if a.log != nil { 135 | a.log.Printf("WB 0x%04X -> %x", pc, b) 136 | } 137 | a.Mem.WriteByte(pc, b) 138 | } 139 | 140 | func (a *AppleII) WriteWord(pc uint16, n uint16) { 141 | if a.log != nil { 142 | a.log.Printf("WW 0x%04X -> %x", pc, n) 143 | } 144 | a.Mem.WriteWord(pc, n) 145 | } 146 | -------------------------------------------------------------------------------- /appleii/display.go: -------------------------------------------------------------------------------- 1 | package appleii 2 | 3 | import ( 4 | "github.com/rivo/tview" 5 | ) 6 | 7 | var baseAddress = [...]uint16{ 8 | 0x400, 9 | 0x480, 10 | 0x500, 11 | 0x580, 12 | 0x600, 13 | 0x680, 14 | 0x700, 15 | 0x780, 16 | 0x428, 17 | 0x4A8, 18 | 0x528, 19 | 0x5A8, 20 | 0x628, 21 | 0x6A8, 22 | 0x728, 23 | 0x7A8, 24 | 0x450, 25 | 0x4D0, 26 | 0x550, 27 | 0x5D0, 28 | 0x650, 29 | 0x6D0, 30 | 0x750, 31 | 0x7D0, 32 | } 33 | 34 | func (a *AppleII) initDisplay() { 35 | a.pixelMap = map[uint16]*tview.TableCell{} 36 | for i, ba := range baseAddress { 37 | for j := uint16(0); j < 40; j++ { 38 | a.pixelMap[ba+j] = a.in.GetCell(i, int(j)) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /appleii/keyboard.go: -------------------------------------------------------------------------------- 1 | package appleii 2 | 3 | import "github.com/gdamore/tcell/v2" 4 | 5 | const ( 6 | KeyboardData = 0xc000 7 | ClearKeyStrobe = 0xc010 8 | 9 | keyIRQ = 1 10 | keyIRQMask = 0xff &^ keyIRQ 11 | ) 12 | 13 | func (a *AppleII) uiKeyPress(event *tcell.EventKey) *tcell.EventKey { 14 | a.cpu.IRQ |= keyIRQ 15 | var b byte 16 | switch event.Key() { 17 | default: 18 | b = byte(event.Rune()) 19 | } 20 | a.Mem[KeyboardData] = b | 0x80 // apple ii use higher 21 | return event 22 | } 23 | -------------------------------------------------------------------------------- /buildos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ue 4 | 5 | rm -rf zhuos/src/*.out 6 | 7 | for f in zhuos/src/*.s 8 | do 9 | if [ "$f" != 'zhuos/src/symbols.s' ]; then 10 | echo $f 11 | go run cmd/asm/main.go -i $f 12 | fi 13 | done 14 | 15 | go run cmd/link/main.go z.dat zhuos/src/*.out 16 | -------------------------------------------------------------------------------- /cmd/a2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "go6502/appleii" 7 | "go6502/zhuos/zp" 8 | "io" 9 | "log" 10 | "os" 11 | "strings" 12 | ) 13 | 14 | var ( 15 | conf = flag.String("c", "", "config json file") 16 | rom = flag.String("r", "", "ROM file path(ZhuOS only)") 17 | ) 18 | 19 | const defConf = `{"refresh_ms":0, "log":"a2.log"}` 20 | 21 | func main() { 22 | flag.Parse() 23 | var rd io.Reader 24 | if *conf == "" { 25 | rd = strings.NewReader(defConf) 26 | } 27 | 28 | com := appleii.New() 29 | if err := com.Init(rd); err != nil { 30 | log.Fatal(err) 31 | } 32 | 33 | if *rom != "" { 34 | fd, err := os.Open(*rom) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | zf := &zp.ZFile{} 39 | err = zp.Decode(fd, zf) 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | for _, prog := range zf.L { 44 | fmt.Printf("%04x=>%x\n", prog.Offset, prog.Prog) 45 | copy(com.Mem[prog.Offset:], prog.Prog) 46 | } 47 | } 48 | 49 | com.Run() 50 | } 51 | -------------------------------------------------------------------------------- /cmd/asm/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "go6502/lisa" 7 | "log" 8 | "os" 9 | "path/filepath" 10 | ) 11 | 12 | var ( 13 | in = flag.String("i", "", "input file") 14 | out = flag.String("o", "", "output object file") 15 | debugParse = flag.Bool("L", false, "print instruction") 16 | debugCompiled = flag.Bool("C", false, "print instruction") 17 | zobj = flag.Bool("z", true, "Zhu OS obj file ") 18 | ) 19 | 20 | func main() { 21 | flag.Parse() 22 | if *in == "" { 23 | log.Println("input file not found") 24 | flag.Usage() 25 | return 26 | } 27 | 28 | ext := filepath.Ext(*in) 29 | 30 | inf := *in 31 | of := *out 32 | if of == "" { 33 | of = inf[:len(inf)-len(ext)] + ".out" 34 | } 35 | err := load(*in, of) 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | 40 | } 41 | 42 | func load(in, of string) (err error) { 43 | ipf, err := os.Open(in) 44 | if err != nil { 45 | return 46 | } 47 | defer ipf.Close() 48 | 49 | ol, err := lisa.Parse(ipf) 50 | if err != nil { 51 | return err 52 | } 53 | 54 | if *debugParse { 55 | for i := range ol { 56 | if ol[i].Mnemonic == 0 && ol[i].Comment != "" { 57 | continue 58 | } 59 | fmt.Println(ol[i]) 60 | } 61 | } 62 | 63 | outf, err := os.OpenFile(of, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0700) 64 | if err != nil { 65 | return 66 | } 67 | defer outf.Close() 68 | err = lisa.Compile(ol, outf, *zobj, *debugCompiled) 69 | 70 | return 71 | } 72 | -------------------------------------------------------------------------------- /cmd/link/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "go6502/lisa" 7 | "go6502/zhuos/zp" 8 | "log" 9 | "os" 10 | ) 11 | 12 | const conf = `{"text":4096, "bss":40960, "data":45056}` 13 | 14 | var debugList = flag.Bool("L", true, "list symbols") 15 | 16 | func main() { 17 | fl := os.Args 18 | if len(fl) < 3 { 19 | log.Fatal("link: out [obj file list...]") 20 | } 21 | l, err := lisa.NewLink(fl[2:], conf) 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | l.Load() 26 | l.Resolve() 27 | for _, blk := range l.Block { 28 | for _, sym := range blk.List { 29 | if sym.Type == lisa.Constants { 30 | continue 31 | } 32 | if *debugList { 33 | fmt.Println(sym) 34 | } 35 | } 36 | } 37 | zf := &zp.ZFile{} 38 | 39 | for _, blk := range l.Block { 40 | if blk.Size == 0 { 41 | continue 42 | } 43 | zp := &zp.ZProg{ 44 | Offset: blk.Origin.Addr, 45 | } 46 | for _, sym := range blk.List { 47 | if lisa.IsNonAddress(sym.Stmt.Mnemonic) { 48 | continue 49 | } 50 | data, err := sym.Data() 51 | if err != nil { 52 | l.Errorf("encode:%s", err) 53 | continue 54 | } 55 | zp.Prog = append(zp.Prog, data...) 56 | } 57 | zf.L = append(zf.L, zp) 58 | } 59 | of, err := os.OpenFile(fl[1], os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600) 60 | if err != nil { 61 | log.Fatal(err) 62 | } 63 | defer of.Close() 64 | err = zp.Encode(of, zf) 65 | if err != nil { 66 | log.Fatal(err) 67 | } 68 | 69 | if l.ErrCount > 0 { 70 | log.Fatal("link failed") 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /cmd/nm/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "go6502/zhuos/zp" 6 | "log" 7 | "os" 8 | ) 9 | 10 | const hdrTmp = `|-- PO:0x%04X 11 | +-- PS:0x%04X 12 | ` 13 | 14 | func main() { 15 | if len(os.Args) < 2 { 16 | log.Fatal("nm program") 17 | } 18 | fd, err := os.Open(os.Args[1]) 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | zf := &zp.ZFile{} 23 | err = zp.Decode(fd, zf) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | for _, hdr := range zf.L { 29 | fmt.Printf(hdrTmp, hdr.Offset, len(hdr.Prog)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /cmd/objdump/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "go6502/lisa" 7 | "io/ioutil" 8 | "log" 9 | ) 10 | 11 | var ( 12 | file = flag.String("b", "", "binary file") 13 | fromI = flag.Int("from", 0, "offset from") 14 | toI = flag.Int("to", 0, "offset to") 15 | ) 16 | 17 | func main() { 18 | flag.Parse() 19 | if *file == "" { 20 | log.Println(*file, *fromI, *toI) 21 | flag.Usage() 22 | return 23 | } 24 | 25 | d, err := ioutil.ReadFile(*file) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | from := *fromI 31 | if from > len(d) { 32 | from = len(d) 33 | } 34 | to := *toI 35 | if to > len(d) { 36 | to = len(d) 37 | } 38 | 39 | sl, err := lisa.Disasm(d[from:to]) 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | 44 | for _, s := range sl { 45 | fmt.Println(s.Mnemonic, s.Oper) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /cmd/objdump/test.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mengzhuo/go6502/f15b30732501f2f1e48422b840c98b7743a390c3/cmd/objdump/test.bin -------------------------------------------------------------------------------- /cpu/cpu.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "go6502/ins" 7 | "log" 8 | "time" 9 | ) 10 | 11 | const ( 12 | debug = false 13 | pss = "CZIDB-VN" 14 | 15 | targetCycle = 98321791 16 | targetPC = 0x406 17 | ) 18 | 19 | const ( 20 | FlagCarry uint8 = 1 << iota 21 | FlagZero 22 | FlagIRQDisable 23 | FlagDecimalMode 24 | FlagBreak 25 | FlagUnused 26 | FlagOverflow 27 | FlagNegtive 28 | ) 29 | 30 | type CPU struct { 31 | totalCycles time.Duration 32 | DurPerCycles time.Duration 33 | 34 | NMI chan bool // edge trigger 35 | Reset chan bool // edge trigger 36 | IRQ uint8 // level trigger zero means no irq 37 | Mem Mem 38 | 39 | log *log.Logger 40 | 41 | irqVec uint16 42 | nmiVec uint16 43 | 44 | PC uint16 45 | AC uint8 46 | RX uint8 47 | RY uint8 48 | PS uint8 49 | IR uint8 // instruction register 50 | sp uint8 51 | } 52 | 53 | func New(log *log.Logger) (c *CPU) { 54 | c = &CPU{ 55 | NMI: make(chan bool, 1024), 56 | Reset: make(chan bool), 57 | log: log, 58 | } 59 | // pagetable.com/?p=410 60 | c.irqVec = 0xfffe 61 | c.nmiVec = 0xfffa 62 | return 63 | } 64 | 65 | func (c *CPU) SP() uint16 { 66 | return uint16(c.sp) | uint16(0x100) 67 | } 68 | 69 | func (c *CPU) String() string { 70 | var t []byte 71 | for i := 0; i < 8; i++ { 72 | if c.PS&(1< targetCycle { 143 | break 144 | } 145 | 146 | if c.PC == targetPC { 147 | break 148 | } 149 | } 150 | 151 | // the instruction didn't change PC 152 | if c.PC == prev { 153 | return c.Fault("infinite loop") 154 | } 155 | } 156 | return 157 | } 158 | 159 | func (c *CPU) Fault(s string) error { 160 | return errors.New("FAULT:" + s) 161 | } 162 | 163 | func (c *CPU) setZN(x uint8) { 164 | if x == 0 { 165 | c.PS |= FlagZero 166 | } else { 167 | c.PS &= ^FlagZero 168 | } 169 | if x&FlagNegtive == 0 { 170 | c.PS &= ^FlagNegtive 171 | } else { 172 | c.PS |= FlagNegtive 173 | } 174 | } 175 | 176 | // simple version of {bytes,bufio}.Reader 177 | type Mem interface { 178 | ReadByte(pc uint16) (b uint8) 179 | ReadWord(pc uint16) (n uint16) 180 | WriteByte(pc uint16, b uint8) 181 | WriteWord(pc uint16, n uint16) 182 | } 183 | -------------------------------------------------------------------------------- /cpu/cpu_test.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | "compress/gzip" 5 | "io/ioutil" 6 | "os" 7 | "testing" 8 | ) 9 | 10 | func TestBinFunction(t *testing.T) { 11 | f, err := os.Open("testdata/6502_functional_test.bin.gz") 12 | if err != nil { 13 | t.Skip("no raw data", err) 14 | } 15 | defer f.Close() 16 | 17 | zr, err := gzip.NewReader(f) 18 | if err != nil { 19 | t.Fatal("gzip", err) 20 | } 21 | defer zr.Close() 22 | zr.Multistream(false) 23 | 24 | data, err := ioutil.ReadAll(zr) 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | m := &SimpleMem{} 29 | n := copy(m[:], data) 30 | if err != nil || n != 0x10000 { 31 | t.Fatal(err, n) 32 | } 33 | 34 | //log := log.New(os.Stderr, "cpu", log.LstdFlags) 35 | c := New(nil) 36 | c.PC = 0x400 37 | err = c.Run(m) 38 | if err == nil { 39 | t.Fatal(err, c) 40 | } 41 | if c.PC != 0x3474 { 42 | t.Fatal(err, c) 43 | } 44 | } 45 | 46 | func TestHelloWorld(t *testing.T) { 47 | f, err := os.Open("../lisa/testdata/hello_world.out") 48 | if err != nil { 49 | t.Skip("no raw data", err) 50 | } 51 | defer f.Close() 52 | data, err := ioutil.ReadAll(f) 53 | if err != nil { 54 | t.Fatal(err) 55 | } 56 | m := &SimpleMem{} 57 | copy(m[0x400:], data) 58 | //log := log.New(os.Stderr, "cpu", log.LstdFlags) 59 | c := New(nil) 60 | c.PC = 0x400 61 | err = c.Run(m) 62 | t.Logf("%18q", string(m[0x42:0x42+18])) 63 | } 64 | -------------------------------------------------------------------------------- /cpu/logic.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | "fmt" 5 | "go6502/ins" 6 | "time" 7 | ) 8 | 9 | func (c *CPU) insHandler(i ins.Ins) (cycles time.Duration, err error) { 10 | 11 | var addr uint16 12 | var oper uint8 13 | 14 | switch i.Name { 15 | default: 16 | err = fmt.Errorf("invalid name") 17 | return 18 | case ins.NOP: 19 | return 20 | case ins.LDA: 21 | c.AC, cycles = c.getOper(i.Mode) 22 | c.setZN(c.AC) 23 | case ins.LDX: 24 | c.RX, cycles = c.getOper(i.Mode) 25 | c.setZN(c.RX) 26 | case ins.LDY: 27 | c.RY, cycles = c.getOper(i.Mode) 28 | c.setZN(c.RY) 29 | case ins.STA: 30 | addr, cycles = c.getAddr(i.Mode) 31 | c.Mem.WriteByte(addr, c.AC) 32 | case ins.STX: 33 | addr, cycles = c.getAddr(i.Mode) 34 | c.Mem.WriteByte(addr, c.RX) 35 | case ins.STY: 36 | addr, cycles = c.getAddr(i.Mode) 37 | c.Mem.WriteByte(addr, c.RY) 38 | 39 | // Stack Operations 40 | case ins.TSX: 41 | c.RX = c.sp 42 | c.setZN(c.RX) 43 | case ins.TXS: 44 | c.sp = c.RX 45 | case ins.PHA: 46 | c.pushByteToStack(c.AC) 47 | case ins.PHP: 48 | c.pushByteToStack(c.PS | FlagBreak | FlagUnused) 49 | case ins.PLA: 50 | c.AC = c.popByteFromStack() 51 | c.setZN(c.AC) 52 | case ins.PLP: 53 | c.PS = c.popByteFromStack() 54 | c.PS &= ^(FlagBreak | FlagUnused) 55 | 56 | // Register Transfer 57 | case ins.TAX: 58 | c.RX = c.AC 59 | c.setZN(c.RX) 60 | case ins.TAY: 61 | c.RY = c.AC 62 | c.setZN(c.RY) 63 | case ins.TXA: 64 | c.AC = c.RX 65 | c.setZN(c.AC) 66 | case ins.TYA: 67 | c.AC = c.RY 68 | c.setZN(c.AC) 69 | 70 | // Logical 71 | case ins.AND: 72 | oper, cycles = c.getOper(i.Mode) 73 | c.AC &= oper 74 | c.setZN(c.AC) 75 | case ins.ORA: 76 | oper, cycles = c.getOper(i.Mode) 77 | c.AC |= oper 78 | c.setZN(c.AC) 79 | case ins.EOR: 80 | oper, cycles = c.getOper(i.Mode) 81 | c.AC ^= oper 82 | c.setZN(c.AC) 83 | case ins.BIT: 84 | oper, cycles = c.getOper(i.Mode) 85 | 86 | if c.AC&oper == 0 { 87 | c.PS |= FlagZero 88 | } else { 89 | c.PS &= ^FlagZero 90 | } 91 | 92 | if oper&FlagNegtive != 0 { 93 | c.PS |= FlagNegtive 94 | } else { 95 | c.PS &= ^FlagNegtive 96 | } 97 | 98 | if oper&FlagOverflow != 0 { 99 | c.PS |= FlagOverflow 100 | } else { 101 | c.PS &= ^FlagOverflow 102 | } 103 | 104 | // Arithmetic 105 | case ins.ADC, ins.SBC: 106 | oper, cycles = c.getOper(i.Mode) 107 | if i.Name == ins.SBC { 108 | oper = ^oper 109 | } 110 | 111 | if c.PS&FlagDecimalMode != 0 { 112 | err = fmt.Errorf("no decimal mode") 113 | return 114 | } 115 | 116 | sameSigned := (c.AC^oper)&FlagNegtive == 0 117 | sum := uint16(c.AC) 118 | sum += uint16(oper) 119 | if c.PS&FlagCarry != 0 { 120 | sum += 1 121 | } 122 | c.AC = uint8(sum & 0xff) 123 | c.setZN(c.AC) 124 | if sum > 0xff { 125 | c.PS |= FlagCarry 126 | } else { 127 | c.PS &= ^FlagCarry 128 | } 129 | 130 | if sameSigned && ((c.AC^oper)&FlagNegtive != 0) { 131 | c.PS |= FlagOverflow 132 | } else { 133 | c.PS &= ^FlagOverflow 134 | } 135 | case ins.CMP: 136 | oper, cycles = c.getOper(i.Mode) 137 | if c.AC >= oper { 138 | c.PS |= FlagCarry 139 | } else { 140 | c.PS &= ^FlagCarry 141 | } 142 | v := c.AC - oper 143 | c.setZN(v) 144 | case ins.CPX: 145 | oper, cycles = c.getOper(i.Mode) 146 | if c.RX >= oper { 147 | c.PS |= FlagCarry 148 | } else { 149 | c.PS &= ^FlagCarry 150 | } 151 | v := c.RX - oper 152 | c.setZN(v) 153 | case ins.CPY: 154 | oper, cycles = c.getOper(i.Mode) 155 | if c.RY >= oper { 156 | c.PS |= FlagCarry 157 | } else { 158 | c.PS &= ^FlagCarry 159 | } 160 | v := c.RY - oper 161 | c.setZN(v) 162 | 163 | // Increments & Decrements 164 | case ins.INC, ins.DEC: 165 | addr, cycles = c.getAddr(i.Mode) 166 | oper := c.Mem.ReadByte(addr) 167 | if i.Name == ins.DEC { 168 | oper -= 1 169 | } else { 170 | oper += 1 171 | } 172 | c.setZN(oper) 173 | c.Mem.WriteByte(addr, oper) 174 | case ins.INX: 175 | c.RX++ 176 | c.setZN(c.RX) 177 | case ins.INY: 178 | c.RY++ 179 | c.setZN(c.RY) 180 | case ins.DEX: 181 | c.RX-- 182 | c.setZN(c.RX) 183 | case ins.DEY: 184 | c.RY-- 185 | c.setZN(c.RY) 186 | 187 | // Shifts 188 | case ins.ASL: 189 | if i.Mode == ins.Accumulator { 190 | if c.AC&(1<<7) != 0 { 191 | c.PS |= FlagCarry 192 | } else { 193 | c.PS &= ^FlagCarry 194 | } 195 | c.AC <<= 1 196 | c.setZN(c.AC) 197 | return 198 | } 199 | addr, cycles = c.getAddr(i.Mode) 200 | oper = c.Mem.ReadByte(addr) 201 | if oper&(1<<7) != 0 { 202 | c.PS |= FlagCarry 203 | } else { 204 | c.PS &= ^FlagCarry 205 | } 206 | oper <<= 1 207 | c.setZN(oper) 208 | c.Mem.WriteByte(addr, oper) 209 | case ins.LSR: 210 | if i.Mode == ins.Accumulator { 211 | if c.AC&1 != 0 { 212 | c.PS |= FlagCarry 213 | } else { 214 | c.PS &= ^FlagCarry 215 | } 216 | c.AC >>= 1 217 | c.setZN(c.AC) 218 | return 219 | } 220 | addr, cycles = c.getAddr(i.Mode) 221 | oper = c.Mem.ReadByte(addr) 222 | if oper&1 != 0 { 223 | c.PS |= FlagCarry 224 | } else { 225 | c.PS &= ^FlagCarry 226 | } 227 | oper >>= 1 228 | c.setZN(oper) 229 | c.Mem.WriteByte(addr, oper) 230 | 231 | case ins.ROL: 232 | carry := false 233 | if i.Mode == ins.Accumulator { 234 | 235 | if c.AC&FlagNegtive != 0 { 236 | carry = true 237 | } 238 | c.AC <<= 1 239 | if c.PS&FlagCarry != 0 { 240 | c.AC |= 1 241 | } 242 | if carry { 243 | c.PS |= FlagCarry 244 | } else { 245 | c.PS &= ^FlagCarry 246 | } 247 | 248 | c.setZN(c.AC) 249 | return 250 | } 251 | 252 | addr, cycles = c.getAddr(i.Mode) 253 | oper = c.Mem.ReadByte(addr) 254 | if oper&FlagNegtive != 0 { 255 | carry = true 256 | } 257 | oper <<= 1 258 | if c.PS&FlagCarry != 0 { 259 | oper |= 1 260 | } 261 | if carry { 262 | c.PS |= FlagCarry 263 | } else { 264 | c.PS &= ^FlagCarry 265 | } 266 | c.setZN(oper) 267 | c.Mem.WriteByte(addr, oper) 268 | 269 | case ins.ROR: 270 | carry := false 271 | if i.Mode == ins.Accumulator { 272 | if c.AC&1 != 0 { 273 | carry = true 274 | } 275 | 276 | c.AC >>= 1 277 | if c.PS&FlagCarry != 0 { 278 | c.AC |= FlagNegtive 279 | } 280 | if carry { 281 | c.PS |= FlagCarry 282 | } else { 283 | c.PS &= ^FlagCarry 284 | } 285 | c.setZN(c.AC) 286 | return 287 | } 288 | 289 | addr, cycles = c.getAddr(i.Mode) 290 | oper = c.Mem.ReadByte(addr) 291 | if oper&1 != 0 { 292 | carry = true 293 | } 294 | 295 | oper >>= 1 296 | if c.PS&FlagCarry != 0 { 297 | oper |= FlagNegtive 298 | } 299 | if carry { 300 | c.PS |= FlagCarry 301 | } else { 302 | c.PS &= ^FlagCarry 303 | } 304 | c.setZN(oper) 305 | c.Mem.WriteByte(addr, oper) 306 | 307 | // Jumps & Calls 308 | case ins.JMP: 309 | addr, cycles = c.getAddr(i.Mode) 310 | c.PC = addr 311 | case ins.JSR: 312 | addr, cycles = c.getAddr(i.Mode) 313 | c.pushWordToStack(c.PC - 1) 314 | c.PC = addr 315 | case ins.RTS: 316 | addr = c.popWordFromStack() 317 | c.PC = addr + 1 318 | 319 | // Branches 320 | case ins.BCC: 321 | cycles = c.BranchIf(i, FlagCarry, false) 322 | case ins.BCS: 323 | cycles = c.BranchIf(i, FlagCarry, true) 324 | case ins.BEQ: 325 | cycles = c.BranchIf(i, FlagZero, true) 326 | case ins.BMI: 327 | cycles = c.BranchIf(i, FlagNegtive, true) 328 | case ins.BNE: 329 | cycles = c.BranchIf(i, FlagZero, false) 330 | case ins.BPL: 331 | cycles = c.BranchIf(i, FlagNegtive, false) 332 | case ins.BVC: 333 | cycles = c.BranchIf(i, FlagOverflow, false) 334 | case ins.BVS: 335 | cycles = c.BranchIf(i, FlagOverflow, true) 336 | 337 | // Status Flag Changes 338 | case ins.CLC: 339 | c.PS &= ^FlagCarry 340 | case ins.CLD: 341 | c.PS &= ^FlagDecimalMode 342 | case ins.CLI: 343 | c.PS &= ^FlagIRQDisable 344 | case ins.CLV: 345 | c.PS &= ^FlagOverflow 346 | case ins.SEC: 347 | c.PS |= FlagCarry 348 | case ins.SED: 349 | c.PS |= FlagDecimalMode 350 | case ins.SEI: 351 | c.PS |= FlagIRQDisable 352 | 353 | // System Functions 354 | case ins.BRK: 355 | c.pushWordToStack(c.PC + 1) 356 | c.pushByteToStack(c.PS | FlagBreak | FlagUnused) 357 | c.PC = c.Mem.ReadWord(c.irqVec) 358 | c.PS |= FlagBreak | FlagIRQDisable 359 | case ins.RTI: 360 | c.PS = c.popByteFromStack() & ^(FlagBreak | FlagUnused) 361 | c.PC = c.popWordFromStack() 362 | } 363 | return 364 | } 365 | 366 | func (c *CPU) BranchIf(i ins.Ins, flag uint8, set bool) (cycles time.Duration) { 367 | var ok bool 368 | if set { 369 | ok = flag&c.PS != 0 370 | } else { 371 | ok = flag&c.PS == 0 372 | } 373 | 374 | if !ok { 375 | cycles = 1 376 | return 377 | } 378 | 379 | oper := c.Mem.ReadByte(c.PC - 1) 380 | old := c.PC - 1 381 | cycles = 1 382 | if oper&FlagNegtive == 0 { 383 | c.PC += uint16(oper) 384 | } else { 385 | c.PC = c.PC - uint16(0xff^oper) - 1 386 | } 387 | 388 | // cross page 389 | if (old^c.PC)>>8 != 0 { 390 | cycles += 1 391 | } 392 | return 393 | } 394 | 395 | func (c *CPU) getAddr(mode ins.Mode) (addr uint16, cycles time.Duration) { 396 | m := c.Mem 397 | switch mode { 398 | default: 399 | panic("no handler") 400 | case ins.Immediate: 401 | addr = c.PC - 1 402 | case ins.ZeroPage: 403 | addr = uint16(m.ReadByte(c.PC - 1)) 404 | case ins.ZeroPageX: 405 | // zero page should be wrapped 406 | zp := m.ReadByte(c.PC - 1) 407 | addr = uint16(c.RX + zp) 408 | case ins.ZeroPageY: 409 | // zero page should be wrapped 410 | zp := m.ReadByte(c.PC - 1) 411 | addr = uint16(c.RY + zp) 412 | case ins.Absolute: 413 | addr = m.ReadWord(c.PC - 2) 414 | case ins.AbsoluteX: 415 | addr = m.ReadWord(c.PC - 2) 416 | addrx := uint16(c.RX) + addr 417 | if addr^addrx>>8 != 0 { 418 | cycles += 1 419 | } 420 | addr = addrx 421 | case ins.AbsoluteY: 422 | addr = m.ReadWord(c.PC - 2) 423 | addry := uint16(c.RY) + addr 424 | if addr^addry>>8 != 0 { 425 | cycles += 1 426 | } 427 | addr = addry 428 | case ins.Indirect: 429 | addr = m.ReadWord(c.PC - 2) 430 | addr = m.ReadWord(addr) 431 | case ins.IndirectX: 432 | zaddr := m.ReadByte(c.PC - 1) 433 | addr = uint16(c.RX + zaddr) 434 | // effective addr 435 | addr = m.ReadWord(addr) 436 | case ins.IndirectY: 437 | addr = uint16(m.ReadByte(c.PC - 1)) 438 | // effective addr 439 | addr = m.ReadWord(addr) 440 | addry := addr + uint16(c.RY) 441 | if (addr^addry)>>8 != 0 { 442 | cycles += 1 443 | } 444 | addr = addry 445 | } 446 | return 447 | } 448 | 449 | func (c *CPU) getOper(mode ins.Mode) (oper uint8, cycles time.Duration) { 450 | var addr uint16 451 | addr, cycles = c.getAddr(mode) 452 | oper = c.Mem.ReadByte(addr) 453 | return 454 | } 455 | 456 | func (c *CPU) pushByteToStack(value uint8) { 457 | c.Mem.WriteByte(c.SP(), value) 458 | c.sp -= 1 459 | } 460 | 461 | func (c *CPU) popByteFromStack() uint8 { 462 | c.sp += 1 463 | return c.Mem.ReadByte(c.SP()) 464 | } 465 | 466 | func (c *CPU) pushWordToStack(v uint16) { 467 | c.pushByteToStack(uint8(v >> 8)) 468 | c.pushByteToStack(uint8(v & 0xff)) 469 | } 470 | 471 | func (c *CPU) popWordFromStack() (r uint16) { 472 | r = c.Mem.ReadWord(c.SP() + 1) 473 | c.sp += 2 474 | return 475 | } 476 | -------------------------------------------------------------------------------- /cpu/mem.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import "fmt" 4 | 5 | type SimpleMem [65536]byte 6 | 7 | func (s *SimpleMem) ReadByte(pc uint16) (b uint8) { 8 | if debug { 9 | fmt.Printf("RB 0x%04X->0x%02X\n", pc, s[pc]) 10 | } 11 | return s[pc] 12 | } 13 | 14 | func (s *SimpleMem) ReadWord(pc uint16) (n uint16) { 15 | n = uint16(s[pc]) 16 | n |= uint16(s[pc+1]) << 8 17 | if debug { 18 | fmt.Printf("RW 0x%04X->0x%04X\n", pc, n) 19 | } 20 | return 21 | } 22 | 23 | func (s *SimpleMem) WriteByte(pc uint16, b uint8) { 24 | if debug { 25 | fmt.Printf("WB 0x%04X<-0x%02X\n", pc, b) 26 | } 27 | s[pc] = b 28 | } 29 | 30 | func (s *SimpleMem) WriteWord(pc uint16, n uint16) { 31 | s[pc] = uint8(n) 32 | s[pc+1] = uint8(n >> 8) 33 | if debug { 34 | fmt.Printf("WW 0x%04X<-0x%04X\n", pc, n) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /cpu/testdata/6502_functional_test.bin.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mengzhuo/go6502/f15b30732501f2f1e48422b840c98b7743a390c3/cpu/testdata/6502_functional_test.bin.gz -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module go6502 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/gdamore/tcell/v2 v2.2.1 7 | github.com/rivo/tview v0.0.0-20210427112339-00fa6267320c 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= 2 | github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= 3 | github.com/gdamore/tcell/v2 v2.2.1 h1:Gt8wk0jd5pIK2CyXNo/fqwxNWf726j1lQjEDdfbnqTc= 4 | github.com/gdamore/tcell/v2 v2.2.1/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU= 5 | github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 6 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= 7 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 8 | github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= 9 | github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 10 | github.com/rivo/tview v0.0.0-20210427112339-00fa6267320c h1:g9l7gTs/Lydt3b+YaGzwPfeYEU2qZlFXjAt54FRxY0U= 11 | github.com/rivo/tview v0.0.0-20210427112339-00fa6267320c/go.mod h1:Uajo+DndkMDGPzEw/Z92AgJrpx6Rvy2HRo/xP1YbXAI= 12 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 13 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 14 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 15 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 16 | golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY= 17 | golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 18 | golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 19 | golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= 20 | golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 21 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 22 | golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= 23 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 24 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 25 | -------------------------------------------------------------------------------- /ins/gen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from bs4 import BeautifulSoup 4 | 5 | od = ["name", "op", "cycles", "addr_mode", "asm", "bytes", "page"] 6 | 7 | translate = { 8 | "implied":"Implied", 9 | "immediate":"Immediate", 10 | "relative":"Relative", 11 | "accumulator":"Accumulator", 12 | "absolute": "Absolute", 13 | "absolute,X": "AbsoluteX", 14 | "absolute,Y": "AbsoluteY", 15 | "zeropage":"ZeroPage", 16 | "zeropage,X":"ZeroPageX", 17 | "zeropage,Y":"ZeroPageY", 18 | "indirect":"Indirect", 19 | "(indirect,X)":"IndirectX", 20 | "(indirect),Y":"IndirectY", 21 | } 22 | 23 | def parse(f): 24 | bs = BeautifulSoup(f, "html.parser") 25 | l = list() 26 | k = ["addr_mode", "asm", "op", "bytes", "cycles"] 27 | ops = bs.find_all("dl", class_="opcodes")[0] 28 | tbs = ops.find_all("table", {"aria-label":"details"}) 29 | for i in tbs: 30 | for j in i.find_all("tr")[1:]: 31 | oo = dict(zip(k,[k.text for k in j.find_all("td")])) 32 | oo["name"] = oo["asm"].split(" ")[0] 33 | 34 | # NOTE: wrong html 35 | if oo["name"] == "BRK": 36 | oo["cycles"] = "7 " 37 | oo["op"] = "0" 38 | oo["bytes"] = "1" 39 | oo["asm"] = "BRK" 40 | 41 | policy = oo["cycles"][1:] 42 | if policy == "**": 43 | oo["policy"] = "Branch" 44 | elif policy == "* ": 45 | oo["policy"] = "Cross" 46 | else: 47 | oo["policy"] = "None" 48 | 49 | oo["cycles"] = oo["cycles"][0] 50 | oo["addr_mode"] = translate[oo["addr_mode"]] 51 | 52 | l.append(oo) 53 | 54 | return l 55 | 56 | # requires obj that has od 57 | def gen(ol, fp): 58 | tmp = """ 59 | Table[{name}{addr_mode}] = Ins{{ 60 | Name: {name}, 61 | Op: 0x{op}, 62 | Mode: {addr_mode}, 63 | Cycles: {cycles}, 64 | Page: {policy}, 65 | Bytes: {bytes}, 66 | }} 67 | """ 68 | fp.write("""// Code generated by "python gen.py && go fmt"; DO NOT EDIT. 69 | 70 | package ins 71 | const ( 72 | """) 73 | for o in ol: 74 | fp.write("{name}{addr_mode} = 0x{op}\n".format(**o)) 75 | fp.write(")\n") 76 | 77 | fp.write("func init(){\n") 78 | for o in ol: 79 | fp.write(tmp.format(**o)) 80 | fp.write("}\n") 81 | 82 | return 83 | 84 | if __name__ == "__main__": 85 | 86 | l = list() 87 | with open("6502_instruction_set.html") as fp: 88 | l = parse(fp) 89 | 90 | with open("op_gen.go", "w") as fp: 91 | gen(l, fp) 92 | -------------------------------------------------------------------------------- /ins/ins.go: -------------------------------------------------------------------------------- 1 | package ins 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | // http://obelisk.me.uk/6502/instructions.html 11 | 12 | //go:generate stringer -type=Mode 13 | type Mode uint16 14 | 15 | const ( 16 | Implied Mode = 1 << iota 17 | Accumulator 18 | Immediate 19 | ZeroPage 20 | ZeroPageX 21 | ZeroPageY 22 | Relative 23 | Absolute 24 | AbsoluteX 25 | AbsoluteY 26 | Indirect 27 | IndirectX 28 | IndirectY 29 | ) 30 | 31 | //go:generate stringer -type=Name 32 | type Name uint8 33 | 34 | const ( 35 | ADC Name = 1 + iota 36 | AND 37 | ASL 38 | BCC 39 | BCS 40 | BEQ 41 | BIT 42 | BMI 43 | BNE 44 | BPL 45 | BRK 46 | BVC 47 | BVS 48 | CLC 49 | CLD 50 | CLI 51 | CLV 52 | CMP 53 | CPX 54 | CPY 55 | DEC 56 | DEX 57 | DEY 58 | EOR 59 | INC 60 | INX 61 | INY 62 | JMP 63 | JSP 64 | JSR 65 | LDA 66 | LDX 67 | LDY 68 | LSR 69 | NOP 70 | ORA 71 | PHA 72 | PHP 73 | PLA 74 | PLP 75 | ROL 76 | ROR 77 | RTI 78 | RTS 79 | SBC 80 | SEC 81 | SED 82 | SEI 83 | STA 84 | STX 85 | STY 86 | TAX 87 | TAY 88 | TSX 89 | TXA 90 | TXS 91 | TYA 92 | ) 93 | 94 | type Flag uint8 95 | 96 | //go:generate stringer --type=PagePolicy 97 | type PagePolicy uint8 98 | 99 | const ( 100 | None PagePolicy = 0 101 | Cross = 1 102 | Branch = 2 103 | ) 104 | 105 | //go:generate python3 gen.py 106 | type Ins struct { 107 | Cycles time.Duration // base cycle count 108 | Name Name 109 | Op uint8 110 | Mode Mode 111 | Affect Flag 112 | Bytes uint8 113 | Page PagePolicy 114 | } 115 | 116 | func (i Ins) String() string { 117 | if i.Mode == Implied { 118 | return fmt.Sprintf("%s", i.Name) 119 | } 120 | return fmt.Sprintf("%s_%s", i.Name, i.Mode) 121 | } 122 | 123 | var ( 124 | Table = [256]Ins{} 125 | nameCache = map[string][]Ins{} 126 | nameCacheGen = &sync.Once{} 127 | ) 128 | 129 | func GetNameTable(name string, mode string) Ins { 130 | nameCacheGen.Do(func() { 131 | for idx := range Table { 132 | i := Table[idx] 133 | nameCache[i.Name.String()] = append(nameCache[i.Name.String()], i) 134 | } 135 | }) 136 | name = strings.ToUpper(name) 137 | 138 | for _, i := range nameCache[name] { 139 | if i.Mode.String() == mode { 140 | return i 141 | } 142 | } 143 | return Ins{} 144 | } 145 | -------------------------------------------------------------------------------- /ins/mode_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=Mode"; DO NOT EDIT. 2 | 3 | package ins 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[Implied-1] 12 | _ = x[Accumulator-2] 13 | _ = x[Immediate-4] 14 | _ = x[ZeroPage-8] 15 | _ = x[ZeroPageX-16] 16 | _ = x[ZeroPageY-32] 17 | _ = x[Relative-64] 18 | _ = x[Absolute-128] 19 | _ = x[AbsoluteX-256] 20 | _ = x[AbsoluteY-512] 21 | _ = x[Indirect-1024] 22 | _ = x[IndirectX-2048] 23 | _ = x[IndirectY-4096] 24 | } 25 | 26 | const _Mode_name = "ImpliedAccumulatorImmediateZeroPageZeroPageXZeroPageYRelativeAbsoluteAbsoluteXAbsoluteYIndirectIndirectXIndirectY" 27 | 28 | var _Mode_map = map[Mode]string{ 29 | 1: _Mode_name[0:7], 30 | 2: _Mode_name[7:18], 31 | 4: _Mode_name[18:27], 32 | 8: _Mode_name[27:35], 33 | 16: _Mode_name[35:44], 34 | 32: _Mode_name[44:53], 35 | 64: _Mode_name[53:61], 36 | 128: _Mode_name[61:69], 37 | 256: _Mode_name[69:78], 38 | 512: _Mode_name[78:87], 39 | 1024: _Mode_name[87:95], 40 | 2048: _Mode_name[95:104], 41 | 4096: _Mode_name[104:113], 42 | } 43 | 44 | func (i Mode) String() string { 45 | if str, ok := _Mode_map[i]; ok { 46 | return str 47 | } 48 | return "Mode(" + strconv.FormatInt(int64(i), 10) + ")" 49 | } 50 | -------------------------------------------------------------------------------- /ins/name_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=Name"; DO NOT EDIT. 2 | 3 | package ins 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[ADC-1] 12 | _ = x[AND-2] 13 | _ = x[ASL-3] 14 | _ = x[BCC-4] 15 | _ = x[BCS-5] 16 | _ = x[BEQ-6] 17 | _ = x[BIT-7] 18 | _ = x[BMI-8] 19 | _ = x[BNE-9] 20 | _ = x[BPL-10] 21 | _ = x[BRK-11] 22 | _ = x[BVC-12] 23 | _ = x[BVS-13] 24 | _ = x[CLC-14] 25 | _ = x[CLD-15] 26 | _ = x[CLI-16] 27 | _ = x[CLV-17] 28 | _ = x[CMP-18] 29 | _ = x[CPX-19] 30 | _ = x[CPY-20] 31 | _ = x[DEC-21] 32 | _ = x[DEX-22] 33 | _ = x[DEY-23] 34 | _ = x[EOR-24] 35 | _ = x[INC-25] 36 | _ = x[INX-26] 37 | _ = x[INY-27] 38 | _ = x[JMP-28] 39 | _ = x[JSP-29] 40 | _ = x[JSR-30] 41 | _ = x[LDA-31] 42 | _ = x[LDX-32] 43 | _ = x[LDY-33] 44 | _ = x[LSR-34] 45 | _ = x[NOP-35] 46 | _ = x[ORA-36] 47 | _ = x[PHA-37] 48 | _ = x[PHP-38] 49 | _ = x[PLA-39] 50 | _ = x[PLP-40] 51 | _ = x[ROL-41] 52 | _ = x[ROR-42] 53 | _ = x[RTI-43] 54 | _ = x[RTS-44] 55 | _ = x[SBC-45] 56 | _ = x[SEC-46] 57 | _ = x[SED-47] 58 | _ = x[SEI-48] 59 | _ = x[STA-49] 60 | _ = x[STX-50] 61 | _ = x[STY-51] 62 | _ = x[TAX-52] 63 | _ = x[TAY-53] 64 | _ = x[TSX-54] 65 | _ = x[TXA-55] 66 | _ = x[TXS-56] 67 | _ = x[TYA-57] 68 | } 69 | 70 | const _Name_name = "ADCANDASLBCCBCSBEQBITBMIBNEBPLBRKBVCBVSCLCCLDCLICLVCMPCPXCPYDECDEXDEYEORINCINXINYJMPJSPJSRLDALDXLDYLSRNOPORAPHAPHPPLAPLPROLRORRTIRTSSBCSECSEDSEISTASTXSTYTAXTAYTSXTXATXSTYA" 71 | 72 | var _Name_index = [...]uint8{0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126, 129, 132, 135, 138, 141, 144, 147, 150, 153, 156, 159, 162, 165, 168, 171} 73 | 74 | func (i Name) String() string { 75 | i -= 1 76 | if i >= Name(len(_Name_index)-1) { 77 | return "Name(" + strconv.FormatInt(int64(i+1), 10) + ")" 78 | } 79 | return _Name_name[_Name_index[i]:_Name_index[i+1]] 80 | } 81 | -------------------------------------------------------------------------------- /ins/op_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by "python gen.py && go fmt"; DO NOT EDIT. 2 | 3 | package ins 4 | const ( 5 | ADCImmediate = 0x69 6 | ADCZeroPage = 0x65 7 | ADCZeroPageX = 0x75 8 | ADCAbsolute = 0x6D 9 | ADCAbsoluteX = 0x7D 10 | ADCAbsoluteY = 0x79 11 | ADCIndirectX = 0x61 12 | ADCIndirectY = 0x71 13 | ANDImmediate = 0x29 14 | ANDZeroPage = 0x25 15 | ANDZeroPageX = 0x35 16 | ANDAbsolute = 0x2D 17 | ANDAbsoluteX = 0x3D 18 | ANDAbsoluteY = 0x39 19 | ANDIndirectX = 0x21 20 | ANDIndirectY = 0x31 21 | ASLAccumulator = 0x0A 22 | ASLZeroPage = 0x06 23 | ASLZeroPageX = 0x16 24 | ASLAbsolute = 0x0E 25 | ASLAbsoluteX = 0x1E 26 | BCCRelative = 0x90 27 | BCSRelative = 0xB0 28 | BEQRelative = 0xF0 29 | BITZeroPage = 0x24 30 | BITAbsolute = 0x2C 31 | BMIRelative = 0x30 32 | BNERelative = 0xD0 33 | BPLRelative = 0x10 34 | BRKImplied = 0x0 35 | BVCRelative = 0x50 36 | BVSRelative = 0x70 37 | CLCImplied = 0x18 38 | CLDImplied = 0xD8 39 | CLIImplied = 0x58 40 | CLVImplied = 0xB8 41 | CMPImmediate = 0xC9 42 | CMPZeroPage = 0xC5 43 | CMPZeroPageX = 0xD5 44 | CMPAbsolute = 0xCD 45 | CMPAbsoluteX = 0xDD 46 | CMPAbsoluteY = 0xD9 47 | CMPIndirectX = 0xC1 48 | CMPIndirectY = 0xD1 49 | CPXImmediate = 0xE0 50 | CPXZeroPage = 0xE4 51 | CPXAbsolute = 0xEC 52 | CPYImmediate = 0xC0 53 | CPYZeroPage = 0xC4 54 | CPYAbsolute = 0xCC 55 | DECZeroPage = 0xC6 56 | DECZeroPageX = 0xD6 57 | DECAbsolute = 0xCE 58 | DECAbsoluteX = 0xDE 59 | DEXImplied = 0xCA 60 | DEYImplied = 0x88 61 | EORImmediate = 0x49 62 | EORZeroPage = 0x45 63 | EORZeroPageX = 0x55 64 | EORAbsolute = 0x4D 65 | EORAbsoluteX = 0x5D 66 | EORAbsoluteY = 0x59 67 | EORIndirectX = 0x41 68 | EORIndirectY = 0x51 69 | INCZeroPage = 0xE6 70 | INCZeroPageX = 0xF6 71 | INCAbsolute = 0xEE 72 | INCAbsoluteX = 0xFE 73 | INXImplied = 0xE8 74 | INYImplied = 0xC8 75 | JMPAbsolute = 0x4C 76 | JMPIndirect = 0x6C 77 | JSRAbsolute = 0x20 78 | LDAImmediate = 0xA9 79 | LDAZeroPage = 0xA5 80 | LDAZeroPageX = 0xB5 81 | LDAAbsolute = 0xAD 82 | LDAAbsoluteX = 0xBD 83 | LDAAbsoluteY = 0xB9 84 | LDAIndirectX = 0xA1 85 | LDAIndirectY = 0xB1 86 | LDXImmediate = 0xA2 87 | LDXZeroPage = 0xA6 88 | LDXZeroPageY = 0xB6 89 | LDXAbsolute = 0xAE 90 | LDXAbsoluteY = 0xBE 91 | LDYImmediate = 0xA0 92 | LDYZeroPage = 0xA4 93 | LDYZeroPageX = 0xB4 94 | LDYAbsolute = 0xAC 95 | LDYAbsoluteX = 0xBC 96 | LSRAccumulator = 0x4A 97 | LSRZeroPage = 0x46 98 | LSRZeroPageX = 0x56 99 | LSRAbsolute = 0x4E 100 | LSRAbsoluteX = 0x5E 101 | NOPImplied = 0xEA 102 | ORAImmediate = 0x09 103 | ORAZeroPage = 0x05 104 | ORAZeroPageX = 0x15 105 | ORAAbsolute = 0x0D 106 | ORAAbsoluteX = 0x1D 107 | ORAAbsoluteY = 0x19 108 | ORAIndirectX = 0x01 109 | ORAIndirectY = 0x11 110 | PHAImplied = 0x48 111 | PHPImplied = 0x08 112 | PLAImplied = 0x68 113 | PLPImplied = 0x28 114 | ROLAccumulator = 0x2A 115 | ROLZeroPage = 0x26 116 | ROLZeroPageX = 0x36 117 | ROLAbsolute = 0x2E 118 | ROLAbsoluteX = 0x3E 119 | RORAccumulator = 0x6A 120 | RORZeroPage = 0x66 121 | RORZeroPageX = 0x76 122 | RORAbsolute = 0x6E 123 | RORAbsoluteX = 0x7E 124 | RTIImplied = 0x40 125 | RTSImplied = 0x60 126 | SBCImmediate = 0xE9 127 | SBCZeroPage = 0xE5 128 | SBCZeroPageX = 0xF5 129 | SBCAbsolute = 0xED 130 | SBCAbsoluteX = 0xFD 131 | SBCAbsoluteY = 0xF9 132 | SBCIndirectX = 0xE1 133 | SBCIndirectY = 0xF1 134 | SECImplied = 0x38 135 | SEDImplied = 0xF8 136 | SEIImplied = 0x78 137 | STAZeroPage = 0x85 138 | STAZeroPageX = 0x95 139 | STAAbsolute = 0x8D 140 | STAAbsoluteX = 0x9D 141 | STAAbsoluteY = 0x99 142 | STAIndirectX = 0x81 143 | STAIndirectY = 0x91 144 | STXZeroPage = 0x86 145 | STXZeroPageY = 0x96 146 | STXAbsolute = 0x8E 147 | STYZeroPage = 0x84 148 | STYZeroPageX = 0x94 149 | STYAbsolute = 0x8C 150 | TAXImplied = 0xAA 151 | TAYImplied = 0xA8 152 | TSXImplied = 0xBA 153 | TXAImplied = 0x8A 154 | TXSImplied = 0x9A 155 | TYAImplied = 0x98 156 | ) 157 | func init(){ 158 | 159 | Table[ADCImmediate] = Ins{ 160 | Name: ADC, 161 | Op: 0x69, 162 | Mode: Immediate, 163 | Cycles: 2, 164 | Page: None, 165 | Bytes: 2, 166 | } 167 | 168 | Table[ADCZeroPage] = Ins{ 169 | Name: ADC, 170 | Op: 0x65, 171 | Mode: ZeroPage, 172 | Cycles: 3, 173 | Page: None, 174 | Bytes: 2, 175 | } 176 | 177 | Table[ADCZeroPageX] = Ins{ 178 | Name: ADC, 179 | Op: 0x75, 180 | Mode: ZeroPageX, 181 | Cycles: 4, 182 | Page: None, 183 | Bytes: 2, 184 | } 185 | 186 | Table[ADCAbsolute] = Ins{ 187 | Name: ADC, 188 | Op: 0x6D, 189 | Mode: Absolute, 190 | Cycles: 4, 191 | Page: None, 192 | Bytes: 3, 193 | } 194 | 195 | Table[ADCAbsoluteX] = Ins{ 196 | Name: ADC, 197 | Op: 0x7D, 198 | Mode: AbsoluteX, 199 | Cycles: 4, 200 | Page: Cross, 201 | Bytes: 3, 202 | } 203 | 204 | Table[ADCAbsoluteY] = Ins{ 205 | Name: ADC, 206 | Op: 0x79, 207 | Mode: AbsoluteY, 208 | Cycles: 4, 209 | Page: Cross, 210 | Bytes: 3, 211 | } 212 | 213 | Table[ADCIndirectX] = Ins{ 214 | Name: ADC, 215 | Op: 0x61, 216 | Mode: IndirectX, 217 | Cycles: 6, 218 | Page: None, 219 | Bytes: 2, 220 | } 221 | 222 | Table[ADCIndirectY] = Ins{ 223 | Name: ADC, 224 | Op: 0x71, 225 | Mode: IndirectY, 226 | Cycles: 5, 227 | Page: Cross, 228 | Bytes: 2, 229 | } 230 | 231 | Table[ANDImmediate] = Ins{ 232 | Name: AND, 233 | Op: 0x29, 234 | Mode: Immediate, 235 | Cycles: 2, 236 | Page: None, 237 | Bytes: 2, 238 | } 239 | 240 | Table[ANDZeroPage] = Ins{ 241 | Name: AND, 242 | Op: 0x25, 243 | Mode: ZeroPage, 244 | Cycles: 3, 245 | Page: None, 246 | Bytes: 2, 247 | } 248 | 249 | Table[ANDZeroPageX] = Ins{ 250 | Name: AND, 251 | Op: 0x35, 252 | Mode: ZeroPageX, 253 | Cycles: 4, 254 | Page: None, 255 | Bytes: 2, 256 | } 257 | 258 | Table[ANDAbsolute] = Ins{ 259 | Name: AND, 260 | Op: 0x2D, 261 | Mode: Absolute, 262 | Cycles: 4, 263 | Page: None, 264 | Bytes: 3, 265 | } 266 | 267 | Table[ANDAbsoluteX] = Ins{ 268 | Name: AND, 269 | Op: 0x3D, 270 | Mode: AbsoluteX, 271 | Cycles: 4, 272 | Page: Cross, 273 | Bytes: 3, 274 | } 275 | 276 | Table[ANDAbsoluteY] = Ins{ 277 | Name: AND, 278 | Op: 0x39, 279 | Mode: AbsoluteY, 280 | Cycles: 4, 281 | Page: Cross, 282 | Bytes: 3, 283 | } 284 | 285 | Table[ANDIndirectX] = Ins{ 286 | Name: AND, 287 | Op: 0x21, 288 | Mode: IndirectX, 289 | Cycles: 6, 290 | Page: None, 291 | Bytes: 2, 292 | } 293 | 294 | Table[ANDIndirectY] = Ins{ 295 | Name: AND, 296 | Op: 0x31, 297 | Mode: IndirectY, 298 | Cycles: 5, 299 | Page: Cross, 300 | Bytes: 2, 301 | } 302 | 303 | Table[ASLAccumulator] = Ins{ 304 | Name: ASL, 305 | Op: 0x0A, 306 | Mode: Accumulator, 307 | Cycles: 2, 308 | Page: None, 309 | Bytes: 1, 310 | } 311 | 312 | Table[ASLZeroPage] = Ins{ 313 | Name: ASL, 314 | Op: 0x06, 315 | Mode: ZeroPage, 316 | Cycles: 5, 317 | Page: None, 318 | Bytes: 2, 319 | } 320 | 321 | Table[ASLZeroPageX] = Ins{ 322 | Name: ASL, 323 | Op: 0x16, 324 | Mode: ZeroPageX, 325 | Cycles: 6, 326 | Page: None, 327 | Bytes: 2, 328 | } 329 | 330 | Table[ASLAbsolute] = Ins{ 331 | Name: ASL, 332 | Op: 0x0E, 333 | Mode: Absolute, 334 | Cycles: 6, 335 | Page: None, 336 | Bytes: 3, 337 | } 338 | 339 | Table[ASLAbsoluteX] = Ins{ 340 | Name: ASL, 341 | Op: 0x1E, 342 | Mode: AbsoluteX, 343 | Cycles: 7, 344 | Page: None, 345 | Bytes: 3, 346 | } 347 | 348 | Table[BCCRelative] = Ins{ 349 | Name: BCC, 350 | Op: 0x90, 351 | Mode: Relative, 352 | Cycles: 2, 353 | Page: Branch, 354 | Bytes: 2, 355 | } 356 | 357 | Table[BCSRelative] = Ins{ 358 | Name: BCS, 359 | Op: 0xB0, 360 | Mode: Relative, 361 | Cycles: 2, 362 | Page: Branch, 363 | Bytes: 2, 364 | } 365 | 366 | Table[BEQRelative] = Ins{ 367 | Name: BEQ, 368 | Op: 0xF0, 369 | Mode: Relative, 370 | Cycles: 2, 371 | Page: Branch, 372 | Bytes: 2, 373 | } 374 | 375 | Table[BITZeroPage] = Ins{ 376 | Name: BIT, 377 | Op: 0x24, 378 | Mode: ZeroPage, 379 | Cycles: 3, 380 | Page: None, 381 | Bytes: 2, 382 | } 383 | 384 | Table[BITAbsolute] = Ins{ 385 | Name: BIT, 386 | Op: 0x2C, 387 | Mode: Absolute, 388 | Cycles: 4, 389 | Page: None, 390 | Bytes: 3, 391 | } 392 | 393 | Table[BMIRelative] = Ins{ 394 | Name: BMI, 395 | Op: 0x30, 396 | Mode: Relative, 397 | Cycles: 2, 398 | Page: Branch, 399 | Bytes: 2, 400 | } 401 | 402 | Table[BNERelative] = Ins{ 403 | Name: BNE, 404 | Op: 0xD0, 405 | Mode: Relative, 406 | Cycles: 2, 407 | Page: Branch, 408 | Bytes: 2, 409 | } 410 | 411 | Table[BPLRelative] = Ins{ 412 | Name: BPL, 413 | Op: 0x10, 414 | Mode: Relative, 415 | Cycles: 2, 416 | Page: Branch, 417 | Bytes: 2, 418 | } 419 | 420 | Table[BRKImplied] = Ins{ 421 | Name: BRK, 422 | Op: 0x0, 423 | Mode: Implied, 424 | Cycles: 7, 425 | Page: None, 426 | Bytes: 1, 427 | } 428 | 429 | Table[BVCRelative] = Ins{ 430 | Name: BVC, 431 | Op: 0x50, 432 | Mode: Relative, 433 | Cycles: 2, 434 | Page: Branch, 435 | Bytes: 2, 436 | } 437 | 438 | Table[BVSRelative] = Ins{ 439 | Name: BVS, 440 | Op: 0x70, 441 | Mode: Relative, 442 | Cycles: 2, 443 | Page: Branch, 444 | Bytes: 2, 445 | } 446 | 447 | Table[CLCImplied] = Ins{ 448 | Name: CLC, 449 | Op: 0x18, 450 | Mode: Implied, 451 | Cycles: 2, 452 | Page: None, 453 | Bytes: 1, 454 | } 455 | 456 | Table[CLDImplied] = Ins{ 457 | Name: CLD, 458 | Op: 0xD8, 459 | Mode: Implied, 460 | Cycles: 2, 461 | Page: None, 462 | Bytes: 1, 463 | } 464 | 465 | Table[CLIImplied] = Ins{ 466 | Name: CLI, 467 | Op: 0x58, 468 | Mode: Implied, 469 | Cycles: 2, 470 | Page: None, 471 | Bytes: 1, 472 | } 473 | 474 | Table[CLVImplied] = Ins{ 475 | Name: CLV, 476 | Op: 0xB8, 477 | Mode: Implied, 478 | Cycles: 2, 479 | Page: None, 480 | Bytes: 1, 481 | } 482 | 483 | Table[CMPImmediate] = Ins{ 484 | Name: CMP, 485 | Op: 0xC9, 486 | Mode: Immediate, 487 | Cycles: 2, 488 | Page: None, 489 | Bytes: 2, 490 | } 491 | 492 | Table[CMPZeroPage] = Ins{ 493 | Name: CMP, 494 | Op: 0xC5, 495 | Mode: ZeroPage, 496 | Cycles: 3, 497 | Page: None, 498 | Bytes: 2, 499 | } 500 | 501 | Table[CMPZeroPageX] = Ins{ 502 | Name: CMP, 503 | Op: 0xD5, 504 | Mode: ZeroPageX, 505 | Cycles: 4, 506 | Page: None, 507 | Bytes: 2, 508 | } 509 | 510 | Table[CMPAbsolute] = Ins{ 511 | Name: CMP, 512 | Op: 0xCD, 513 | Mode: Absolute, 514 | Cycles: 4, 515 | Page: None, 516 | Bytes: 3, 517 | } 518 | 519 | Table[CMPAbsoluteX] = Ins{ 520 | Name: CMP, 521 | Op: 0xDD, 522 | Mode: AbsoluteX, 523 | Cycles: 4, 524 | Page: Cross, 525 | Bytes: 3, 526 | } 527 | 528 | Table[CMPAbsoluteY] = Ins{ 529 | Name: CMP, 530 | Op: 0xD9, 531 | Mode: AbsoluteY, 532 | Cycles: 4, 533 | Page: Cross, 534 | Bytes: 3, 535 | } 536 | 537 | Table[CMPIndirectX] = Ins{ 538 | Name: CMP, 539 | Op: 0xC1, 540 | Mode: IndirectX, 541 | Cycles: 6, 542 | Page: None, 543 | Bytes: 2, 544 | } 545 | 546 | Table[CMPIndirectY] = Ins{ 547 | Name: CMP, 548 | Op: 0xD1, 549 | Mode: IndirectY, 550 | Cycles: 5, 551 | Page: Cross, 552 | Bytes: 2, 553 | } 554 | 555 | Table[CPXImmediate] = Ins{ 556 | Name: CPX, 557 | Op: 0xE0, 558 | Mode: Immediate, 559 | Cycles: 2, 560 | Page: None, 561 | Bytes: 2, 562 | } 563 | 564 | Table[CPXZeroPage] = Ins{ 565 | Name: CPX, 566 | Op: 0xE4, 567 | Mode: ZeroPage, 568 | Cycles: 3, 569 | Page: None, 570 | Bytes: 2, 571 | } 572 | 573 | Table[CPXAbsolute] = Ins{ 574 | Name: CPX, 575 | Op: 0xEC, 576 | Mode: Absolute, 577 | Cycles: 4, 578 | Page: None, 579 | Bytes: 3, 580 | } 581 | 582 | Table[CPYImmediate] = Ins{ 583 | Name: CPY, 584 | Op: 0xC0, 585 | Mode: Immediate, 586 | Cycles: 2, 587 | Page: None, 588 | Bytes: 2, 589 | } 590 | 591 | Table[CPYZeroPage] = Ins{ 592 | Name: CPY, 593 | Op: 0xC4, 594 | Mode: ZeroPage, 595 | Cycles: 3, 596 | Page: None, 597 | Bytes: 2, 598 | } 599 | 600 | Table[CPYAbsolute] = Ins{ 601 | Name: CPY, 602 | Op: 0xCC, 603 | Mode: Absolute, 604 | Cycles: 4, 605 | Page: None, 606 | Bytes: 3, 607 | } 608 | 609 | Table[DECZeroPage] = Ins{ 610 | Name: DEC, 611 | Op: 0xC6, 612 | Mode: ZeroPage, 613 | Cycles: 5, 614 | Page: None, 615 | Bytes: 2, 616 | } 617 | 618 | Table[DECZeroPageX] = Ins{ 619 | Name: DEC, 620 | Op: 0xD6, 621 | Mode: ZeroPageX, 622 | Cycles: 6, 623 | Page: None, 624 | Bytes: 2, 625 | } 626 | 627 | Table[DECAbsolute] = Ins{ 628 | Name: DEC, 629 | Op: 0xCE, 630 | Mode: Absolute, 631 | Cycles: 6, 632 | Page: None, 633 | Bytes: 3, 634 | } 635 | 636 | Table[DECAbsoluteX] = Ins{ 637 | Name: DEC, 638 | Op: 0xDE, 639 | Mode: AbsoluteX, 640 | Cycles: 7, 641 | Page: None, 642 | Bytes: 3, 643 | } 644 | 645 | Table[DEXImplied] = Ins{ 646 | Name: DEX, 647 | Op: 0xCA, 648 | Mode: Implied, 649 | Cycles: 2, 650 | Page: None, 651 | Bytes: 1, 652 | } 653 | 654 | Table[DEYImplied] = Ins{ 655 | Name: DEY, 656 | Op: 0x88, 657 | Mode: Implied, 658 | Cycles: 2, 659 | Page: None, 660 | Bytes: 1, 661 | } 662 | 663 | Table[EORImmediate] = Ins{ 664 | Name: EOR, 665 | Op: 0x49, 666 | Mode: Immediate, 667 | Cycles: 2, 668 | Page: None, 669 | Bytes: 2, 670 | } 671 | 672 | Table[EORZeroPage] = Ins{ 673 | Name: EOR, 674 | Op: 0x45, 675 | Mode: ZeroPage, 676 | Cycles: 3, 677 | Page: None, 678 | Bytes: 2, 679 | } 680 | 681 | Table[EORZeroPageX] = Ins{ 682 | Name: EOR, 683 | Op: 0x55, 684 | Mode: ZeroPageX, 685 | Cycles: 4, 686 | Page: None, 687 | Bytes: 2, 688 | } 689 | 690 | Table[EORAbsolute] = Ins{ 691 | Name: EOR, 692 | Op: 0x4D, 693 | Mode: Absolute, 694 | Cycles: 4, 695 | Page: None, 696 | Bytes: 3, 697 | } 698 | 699 | Table[EORAbsoluteX] = Ins{ 700 | Name: EOR, 701 | Op: 0x5D, 702 | Mode: AbsoluteX, 703 | Cycles: 4, 704 | Page: Cross, 705 | Bytes: 3, 706 | } 707 | 708 | Table[EORAbsoluteY] = Ins{ 709 | Name: EOR, 710 | Op: 0x59, 711 | Mode: AbsoluteY, 712 | Cycles: 4, 713 | Page: Cross, 714 | Bytes: 3, 715 | } 716 | 717 | Table[EORIndirectX] = Ins{ 718 | Name: EOR, 719 | Op: 0x41, 720 | Mode: IndirectX, 721 | Cycles: 6, 722 | Page: None, 723 | Bytes: 2, 724 | } 725 | 726 | Table[EORIndirectY] = Ins{ 727 | Name: EOR, 728 | Op: 0x51, 729 | Mode: IndirectY, 730 | Cycles: 5, 731 | Page: Cross, 732 | Bytes: 2, 733 | } 734 | 735 | Table[INCZeroPage] = Ins{ 736 | Name: INC, 737 | Op: 0xE6, 738 | Mode: ZeroPage, 739 | Cycles: 5, 740 | Page: None, 741 | Bytes: 2, 742 | } 743 | 744 | Table[INCZeroPageX] = Ins{ 745 | Name: INC, 746 | Op: 0xF6, 747 | Mode: ZeroPageX, 748 | Cycles: 6, 749 | Page: None, 750 | Bytes: 2, 751 | } 752 | 753 | Table[INCAbsolute] = Ins{ 754 | Name: INC, 755 | Op: 0xEE, 756 | Mode: Absolute, 757 | Cycles: 6, 758 | Page: None, 759 | Bytes: 3, 760 | } 761 | 762 | Table[INCAbsoluteX] = Ins{ 763 | Name: INC, 764 | Op: 0xFE, 765 | Mode: AbsoluteX, 766 | Cycles: 7, 767 | Page: None, 768 | Bytes: 3, 769 | } 770 | 771 | Table[INXImplied] = Ins{ 772 | Name: INX, 773 | Op: 0xE8, 774 | Mode: Implied, 775 | Cycles: 2, 776 | Page: None, 777 | Bytes: 1, 778 | } 779 | 780 | Table[INYImplied] = Ins{ 781 | Name: INY, 782 | Op: 0xC8, 783 | Mode: Implied, 784 | Cycles: 2, 785 | Page: None, 786 | Bytes: 1, 787 | } 788 | 789 | Table[JMPAbsolute] = Ins{ 790 | Name: JMP, 791 | Op: 0x4C, 792 | Mode: Absolute, 793 | Cycles: 3, 794 | Page: None, 795 | Bytes: 3, 796 | } 797 | 798 | Table[JMPIndirect] = Ins{ 799 | Name: JMP, 800 | Op: 0x6C, 801 | Mode: Indirect, 802 | Cycles: 5, 803 | Page: None, 804 | Bytes: 3, 805 | } 806 | 807 | Table[JSRAbsolute] = Ins{ 808 | Name: JSR, 809 | Op: 0x20, 810 | Mode: Absolute, 811 | Cycles: 6, 812 | Page: None, 813 | Bytes: 3, 814 | } 815 | 816 | Table[LDAImmediate] = Ins{ 817 | Name: LDA, 818 | Op: 0xA9, 819 | Mode: Immediate, 820 | Cycles: 2, 821 | Page: None, 822 | Bytes: 2, 823 | } 824 | 825 | Table[LDAZeroPage] = Ins{ 826 | Name: LDA, 827 | Op: 0xA5, 828 | Mode: ZeroPage, 829 | Cycles: 3, 830 | Page: None, 831 | Bytes: 2, 832 | } 833 | 834 | Table[LDAZeroPageX] = Ins{ 835 | Name: LDA, 836 | Op: 0xB5, 837 | Mode: ZeroPageX, 838 | Cycles: 4, 839 | Page: None, 840 | Bytes: 2, 841 | } 842 | 843 | Table[LDAAbsolute] = Ins{ 844 | Name: LDA, 845 | Op: 0xAD, 846 | Mode: Absolute, 847 | Cycles: 4, 848 | Page: None, 849 | Bytes: 3, 850 | } 851 | 852 | Table[LDAAbsoluteX] = Ins{ 853 | Name: LDA, 854 | Op: 0xBD, 855 | Mode: AbsoluteX, 856 | Cycles: 4, 857 | Page: Cross, 858 | Bytes: 3, 859 | } 860 | 861 | Table[LDAAbsoluteY] = Ins{ 862 | Name: LDA, 863 | Op: 0xB9, 864 | Mode: AbsoluteY, 865 | Cycles: 4, 866 | Page: Cross, 867 | Bytes: 3, 868 | } 869 | 870 | Table[LDAIndirectX] = Ins{ 871 | Name: LDA, 872 | Op: 0xA1, 873 | Mode: IndirectX, 874 | Cycles: 6, 875 | Page: None, 876 | Bytes: 2, 877 | } 878 | 879 | Table[LDAIndirectY] = Ins{ 880 | Name: LDA, 881 | Op: 0xB1, 882 | Mode: IndirectY, 883 | Cycles: 5, 884 | Page: Cross, 885 | Bytes: 2, 886 | } 887 | 888 | Table[LDXImmediate] = Ins{ 889 | Name: LDX, 890 | Op: 0xA2, 891 | Mode: Immediate, 892 | Cycles: 2, 893 | Page: None, 894 | Bytes: 2, 895 | } 896 | 897 | Table[LDXZeroPage] = Ins{ 898 | Name: LDX, 899 | Op: 0xA6, 900 | Mode: ZeroPage, 901 | Cycles: 3, 902 | Page: None, 903 | Bytes: 2, 904 | } 905 | 906 | Table[LDXZeroPageY] = Ins{ 907 | Name: LDX, 908 | Op: 0xB6, 909 | Mode: ZeroPageY, 910 | Cycles: 4, 911 | Page: None, 912 | Bytes: 2, 913 | } 914 | 915 | Table[LDXAbsolute] = Ins{ 916 | Name: LDX, 917 | Op: 0xAE, 918 | Mode: Absolute, 919 | Cycles: 4, 920 | Page: None, 921 | Bytes: 3, 922 | } 923 | 924 | Table[LDXAbsoluteY] = Ins{ 925 | Name: LDX, 926 | Op: 0xBE, 927 | Mode: AbsoluteY, 928 | Cycles: 4, 929 | Page: Cross, 930 | Bytes: 3, 931 | } 932 | 933 | Table[LDYImmediate] = Ins{ 934 | Name: LDY, 935 | Op: 0xA0, 936 | Mode: Immediate, 937 | Cycles: 2, 938 | Page: None, 939 | Bytes: 2, 940 | } 941 | 942 | Table[LDYZeroPage] = Ins{ 943 | Name: LDY, 944 | Op: 0xA4, 945 | Mode: ZeroPage, 946 | Cycles: 3, 947 | Page: None, 948 | Bytes: 2, 949 | } 950 | 951 | Table[LDYZeroPageX] = Ins{ 952 | Name: LDY, 953 | Op: 0xB4, 954 | Mode: ZeroPageX, 955 | Cycles: 4, 956 | Page: None, 957 | Bytes: 2, 958 | } 959 | 960 | Table[LDYAbsolute] = Ins{ 961 | Name: LDY, 962 | Op: 0xAC, 963 | Mode: Absolute, 964 | Cycles: 4, 965 | Page: None, 966 | Bytes: 3, 967 | } 968 | 969 | Table[LDYAbsoluteX] = Ins{ 970 | Name: LDY, 971 | Op: 0xBC, 972 | Mode: AbsoluteX, 973 | Cycles: 4, 974 | Page: Cross, 975 | Bytes: 3, 976 | } 977 | 978 | Table[LSRAccumulator] = Ins{ 979 | Name: LSR, 980 | Op: 0x4A, 981 | Mode: Accumulator, 982 | Cycles: 2, 983 | Page: None, 984 | Bytes: 1, 985 | } 986 | 987 | Table[LSRZeroPage] = Ins{ 988 | Name: LSR, 989 | Op: 0x46, 990 | Mode: ZeroPage, 991 | Cycles: 5, 992 | Page: None, 993 | Bytes: 2, 994 | } 995 | 996 | Table[LSRZeroPageX] = Ins{ 997 | Name: LSR, 998 | Op: 0x56, 999 | Mode: ZeroPageX, 1000 | Cycles: 6, 1001 | Page: None, 1002 | Bytes: 2, 1003 | } 1004 | 1005 | Table[LSRAbsolute] = Ins{ 1006 | Name: LSR, 1007 | Op: 0x4E, 1008 | Mode: Absolute, 1009 | Cycles: 6, 1010 | Page: None, 1011 | Bytes: 3, 1012 | } 1013 | 1014 | Table[LSRAbsoluteX] = Ins{ 1015 | Name: LSR, 1016 | Op: 0x5E, 1017 | Mode: AbsoluteX, 1018 | Cycles: 7, 1019 | Page: None, 1020 | Bytes: 3, 1021 | } 1022 | 1023 | Table[NOPImplied] = Ins{ 1024 | Name: NOP, 1025 | Op: 0xEA, 1026 | Mode: Implied, 1027 | Cycles: 2, 1028 | Page: None, 1029 | Bytes: 1, 1030 | } 1031 | 1032 | Table[ORAImmediate] = Ins{ 1033 | Name: ORA, 1034 | Op: 0x09, 1035 | Mode: Immediate, 1036 | Cycles: 2, 1037 | Page: None, 1038 | Bytes: 2, 1039 | } 1040 | 1041 | Table[ORAZeroPage] = Ins{ 1042 | Name: ORA, 1043 | Op: 0x05, 1044 | Mode: ZeroPage, 1045 | Cycles: 3, 1046 | Page: None, 1047 | Bytes: 2, 1048 | } 1049 | 1050 | Table[ORAZeroPageX] = Ins{ 1051 | Name: ORA, 1052 | Op: 0x15, 1053 | Mode: ZeroPageX, 1054 | Cycles: 4, 1055 | Page: None, 1056 | Bytes: 2, 1057 | } 1058 | 1059 | Table[ORAAbsolute] = Ins{ 1060 | Name: ORA, 1061 | Op: 0x0D, 1062 | Mode: Absolute, 1063 | Cycles: 4, 1064 | Page: None, 1065 | Bytes: 3, 1066 | } 1067 | 1068 | Table[ORAAbsoluteX] = Ins{ 1069 | Name: ORA, 1070 | Op: 0x1D, 1071 | Mode: AbsoluteX, 1072 | Cycles: 4, 1073 | Page: Cross, 1074 | Bytes: 3, 1075 | } 1076 | 1077 | Table[ORAAbsoluteY] = Ins{ 1078 | Name: ORA, 1079 | Op: 0x19, 1080 | Mode: AbsoluteY, 1081 | Cycles: 4, 1082 | Page: Cross, 1083 | Bytes: 3, 1084 | } 1085 | 1086 | Table[ORAIndirectX] = Ins{ 1087 | Name: ORA, 1088 | Op: 0x01, 1089 | Mode: IndirectX, 1090 | Cycles: 6, 1091 | Page: None, 1092 | Bytes: 2, 1093 | } 1094 | 1095 | Table[ORAIndirectY] = Ins{ 1096 | Name: ORA, 1097 | Op: 0x11, 1098 | Mode: IndirectY, 1099 | Cycles: 5, 1100 | Page: Cross, 1101 | Bytes: 2, 1102 | } 1103 | 1104 | Table[PHAImplied] = Ins{ 1105 | Name: PHA, 1106 | Op: 0x48, 1107 | Mode: Implied, 1108 | Cycles: 3, 1109 | Page: None, 1110 | Bytes: 1, 1111 | } 1112 | 1113 | Table[PHPImplied] = Ins{ 1114 | Name: PHP, 1115 | Op: 0x08, 1116 | Mode: Implied, 1117 | Cycles: 3, 1118 | Page: None, 1119 | Bytes: 1, 1120 | } 1121 | 1122 | Table[PLAImplied] = Ins{ 1123 | Name: PLA, 1124 | Op: 0x68, 1125 | Mode: Implied, 1126 | Cycles: 4, 1127 | Page: None, 1128 | Bytes: 1, 1129 | } 1130 | 1131 | Table[PLPImplied] = Ins{ 1132 | Name: PLP, 1133 | Op: 0x28, 1134 | Mode: Implied, 1135 | Cycles: 4, 1136 | Page: None, 1137 | Bytes: 1, 1138 | } 1139 | 1140 | Table[ROLAccumulator] = Ins{ 1141 | Name: ROL, 1142 | Op: 0x2A, 1143 | Mode: Accumulator, 1144 | Cycles: 2, 1145 | Page: None, 1146 | Bytes: 1, 1147 | } 1148 | 1149 | Table[ROLZeroPage] = Ins{ 1150 | Name: ROL, 1151 | Op: 0x26, 1152 | Mode: ZeroPage, 1153 | Cycles: 5, 1154 | Page: None, 1155 | Bytes: 2, 1156 | } 1157 | 1158 | Table[ROLZeroPageX] = Ins{ 1159 | Name: ROL, 1160 | Op: 0x36, 1161 | Mode: ZeroPageX, 1162 | Cycles: 6, 1163 | Page: None, 1164 | Bytes: 2, 1165 | } 1166 | 1167 | Table[ROLAbsolute] = Ins{ 1168 | Name: ROL, 1169 | Op: 0x2E, 1170 | Mode: Absolute, 1171 | Cycles: 6, 1172 | Page: None, 1173 | Bytes: 3, 1174 | } 1175 | 1176 | Table[ROLAbsoluteX] = Ins{ 1177 | Name: ROL, 1178 | Op: 0x3E, 1179 | Mode: AbsoluteX, 1180 | Cycles: 7, 1181 | Page: None, 1182 | Bytes: 3, 1183 | } 1184 | 1185 | Table[RORAccumulator] = Ins{ 1186 | Name: ROR, 1187 | Op: 0x6A, 1188 | Mode: Accumulator, 1189 | Cycles: 2, 1190 | Page: None, 1191 | Bytes: 1, 1192 | } 1193 | 1194 | Table[RORZeroPage] = Ins{ 1195 | Name: ROR, 1196 | Op: 0x66, 1197 | Mode: ZeroPage, 1198 | Cycles: 5, 1199 | Page: None, 1200 | Bytes: 2, 1201 | } 1202 | 1203 | Table[RORZeroPageX] = Ins{ 1204 | Name: ROR, 1205 | Op: 0x76, 1206 | Mode: ZeroPageX, 1207 | Cycles: 6, 1208 | Page: None, 1209 | Bytes: 2, 1210 | } 1211 | 1212 | Table[RORAbsolute] = Ins{ 1213 | Name: ROR, 1214 | Op: 0x6E, 1215 | Mode: Absolute, 1216 | Cycles: 6, 1217 | Page: None, 1218 | Bytes: 3, 1219 | } 1220 | 1221 | Table[RORAbsoluteX] = Ins{ 1222 | Name: ROR, 1223 | Op: 0x7E, 1224 | Mode: AbsoluteX, 1225 | Cycles: 7, 1226 | Page: None, 1227 | Bytes: 3, 1228 | } 1229 | 1230 | Table[RTIImplied] = Ins{ 1231 | Name: RTI, 1232 | Op: 0x40, 1233 | Mode: Implied, 1234 | Cycles: 6, 1235 | Page: None, 1236 | Bytes: 1, 1237 | } 1238 | 1239 | Table[RTSImplied] = Ins{ 1240 | Name: RTS, 1241 | Op: 0x60, 1242 | Mode: Implied, 1243 | Cycles: 6, 1244 | Page: None, 1245 | Bytes: 1, 1246 | } 1247 | 1248 | Table[SBCImmediate] = Ins{ 1249 | Name: SBC, 1250 | Op: 0xE9, 1251 | Mode: Immediate, 1252 | Cycles: 2, 1253 | Page: None, 1254 | Bytes: 2, 1255 | } 1256 | 1257 | Table[SBCZeroPage] = Ins{ 1258 | Name: SBC, 1259 | Op: 0xE5, 1260 | Mode: ZeroPage, 1261 | Cycles: 3, 1262 | Page: None, 1263 | Bytes: 2, 1264 | } 1265 | 1266 | Table[SBCZeroPageX] = Ins{ 1267 | Name: SBC, 1268 | Op: 0xF5, 1269 | Mode: ZeroPageX, 1270 | Cycles: 4, 1271 | Page: None, 1272 | Bytes: 2, 1273 | } 1274 | 1275 | Table[SBCAbsolute] = Ins{ 1276 | Name: SBC, 1277 | Op: 0xED, 1278 | Mode: Absolute, 1279 | Cycles: 4, 1280 | Page: None, 1281 | Bytes: 3, 1282 | } 1283 | 1284 | Table[SBCAbsoluteX] = Ins{ 1285 | Name: SBC, 1286 | Op: 0xFD, 1287 | Mode: AbsoluteX, 1288 | Cycles: 4, 1289 | Page: Cross, 1290 | Bytes: 3, 1291 | } 1292 | 1293 | Table[SBCAbsoluteY] = Ins{ 1294 | Name: SBC, 1295 | Op: 0xF9, 1296 | Mode: AbsoluteY, 1297 | Cycles: 4, 1298 | Page: Cross, 1299 | Bytes: 3, 1300 | } 1301 | 1302 | Table[SBCIndirectX] = Ins{ 1303 | Name: SBC, 1304 | Op: 0xE1, 1305 | Mode: IndirectX, 1306 | Cycles: 6, 1307 | Page: None, 1308 | Bytes: 2, 1309 | } 1310 | 1311 | Table[SBCIndirectY] = Ins{ 1312 | Name: SBC, 1313 | Op: 0xF1, 1314 | Mode: IndirectY, 1315 | Cycles: 5, 1316 | Page: Cross, 1317 | Bytes: 2, 1318 | } 1319 | 1320 | Table[SECImplied] = Ins{ 1321 | Name: SEC, 1322 | Op: 0x38, 1323 | Mode: Implied, 1324 | Cycles: 2, 1325 | Page: None, 1326 | Bytes: 1, 1327 | } 1328 | 1329 | Table[SEDImplied] = Ins{ 1330 | Name: SED, 1331 | Op: 0xF8, 1332 | Mode: Implied, 1333 | Cycles: 2, 1334 | Page: None, 1335 | Bytes: 1, 1336 | } 1337 | 1338 | Table[SEIImplied] = Ins{ 1339 | Name: SEI, 1340 | Op: 0x78, 1341 | Mode: Implied, 1342 | Cycles: 2, 1343 | Page: None, 1344 | Bytes: 1, 1345 | } 1346 | 1347 | Table[STAZeroPage] = Ins{ 1348 | Name: STA, 1349 | Op: 0x85, 1350 | Mode: ZeroPage, 1351 | Cycles: 3, 1352 | Page: None, 1353 | Bytes: 2, 1354 | } 1355 | 1356 | Table[STAZeroPageX] = Ins{ 1357 | Name: STA, 1358 | Op: 0x95, 1359 | Mode: ZeroPageX, 1360 | Cycles: 4, 1361 | Page: None, 1362 | Bytes: 2, 1363 | } 1364 | 1365 | Table[STAAbsolute] = Ins{ 1366 | Name: STA, 1367 | Op: 0x8D, 1368 | Mode: Absolute, 1369 | Cycles: 4, 1370 | Page: None, 1371 | Bytes: 3, 1372 | } 1373 | 1374 | Table[STAAbsoluteX] = Ins{ 1375 | Name: STA, 1376 | Op: 0x9D, 1377 | Mode: AbsoluteX, 1378 | Cycles: 5, 1379 | Page: None, 1380 | Bytes: 3, 1381 | } 1382 | 1383 | Table[STAAbsoluteY] = Ins{ 1384 | Name: STA, 1385 | Op: 0x99, 1386 | Mode: AbsoluteY, 1387 | Cycles: 5, 1388 | Page: None, 1389 | Bytes: 3, 1390 | } 1391 | 1392 | Table[STAIndirectX] = Ins{ 1393 | Name: STA, 1394 | Op: 0x81, 1395 | Mode: IndirectX, 1396 | Cycles: 6, 1397 | Page: None, 1398 | Bytes: 2, 1399 | } 1400 | 1401 | Table[STAIndirectY] = Ins{ 1402 | Name: STA, 1403 | Op: 0x91, 1404 | Mode: IndirectY, 1405 | Cycles: 6, 1406 | Page: None, 1407 | Bytes: 2, 1408 | } 1409 | 1410 | Table[STXZeroPage] = Ins{ 1411 | Name: STX, 1412 | Op: 0x86, 1413 | Mode: ZeroPage, 1414 | Cycles: 3, 1415 | Page: None, 1416 | Bytes: 2, 1417 | } 1418 | 1419 | Table[STXZeroPageY] = Ins{ 1420 | Name: STX, 1421 | Op: 0x96, 1422 | Mode: ZeroPageY, 1423 | Cycles: 4, 1424 | Page: None, 1425 | Bytes: 2, 1426 | } 1427 | 1428 | Table[STXAbsolute] = Ins{ 1429 | Name: STX, 1430 | Op: 0x8E, 1431 | Mode: Absolute, 1432 | Cycles: 4, 1433 | Page: None, 1434 | Bytes: 3, 1435 | } 1436 | 1437 | Table[STYZeroPage] = Ins{ 1438 | Name: STY, 1439 | Op: 0x84, 1440 | Mode: ZeroPage, 1441 | Cycles: 3, 1442 | Page: None, 1443 | Bytes: 2, 1444 | } 1445 | 1446 | Table[STYZeroPageX] = Ins{ 1447 | Name: STY, 1448 | Op: 0x94, 1449 | Mode: ZeroPageX, 1450 | Cycles: 4, 1451 | Page: None, 1452 | Bytes: 2, 1453 | } 1454 | 1455 | Table[STYAbsolute] = Ins{ 1456 | Name: STY, 1457 | Op: 0x8C, 1458 | Mode: Absolute, 1459 | Cycles: 4, 1460 | Page: None, 1461 | Bytes: 3, 1462 | } 1463 | 1464 | Table[TAXImplied] = Ins{ 1465 | Name: TAX, 1466 | Op: 0xAA, 1467 | Mode: Implied, 1468 | Cycles: 2, 1469 | Page: None, 1470 | Bytes: 1, 1471 | } 1472 | 1473 | Table[TAYImplied] = Ins{ 1474 | Name: TAY, 1475 | Op: 0xA8, 1476 | Mode: Implied, 1477 | Cycles: 2, 1478 | Page: None, 1479 | Bytes: 1, 1480 | } 1481 | 1482 | Table[TSXImplied] = Ins{ 1483 | Name: TSX, 1484 | Op: 0xBA, 1485 | Mode: Implied, 1486 | Cycles: 2, 1487 | Page: None, 1488 | Bytes: 1, 1489 | } 1490 | 1491 | Table[TXAImplied] = Ins{ 1492 | Name: TXA, 1493 | Op: 0x8A, 1494 | Mode: Implied, 1495 | Cycles: 2, 1496 | Page: None, 1497 | Bytes: 1, 1498 | } 1499 | 1500 | Table[TXSImplied] = Ins{ 1501 | Name: TXS, 1502 | Op: 0x9A, 1503 | Mode: Implied, 1504 | Cycles: 2, 1505 | Page: None, 1506 | Bytes: 1, 1507 | } 1508 | 1509 | Table[TYAImplied] = Ins{ 1510 | Name: TYA, 1511 | Op: 0x98, 1512 | Mode: Implied, 1513 | Cycles: 2, 1514 | Page: None, 1515 | Bytes: 1, 1516 | } 1517 | } 1518 | -------------------------------------------------------------------------------- /ins/pagepolicy_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer --type=PagePolicy"; DO NOT EDIT. 2 | 3 | package ins 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[None-0] 12 | } 13 | 14 | const _PagePolicy_name = "None" 15 | 16 | var _PagePolicy_index = [...]uint8{0, 4} 17 | 18 | func (i PagePolicy) String() string { 19 | if i >= PagePolicy(len(_PagePolicy_index)-1) { 20 | return "PagePolicy(" + strconv.FormatInt(int64(i), 10) + ")" 21 | } 22 | return _PagePolicy_name[_PagePolicy_index[i]:_PagePolicy_index[i+1]] 23 | } 24 | -------------------------------------------------------------------------------- /lisa/compile.go: -------------------------------------------------------------------------------- 1 | package lisa 2 | 3 | import ( 4 | "encoding/binary" 5 | ehex "encoding/hex" 6 | "fmt" 7 | "go6502/ins" 8 | "io" 9 | "log" 10 | ) 11 | 12 | //go:generate stringer --type=SType 13 | type SType uint8 14 | 15 | const ( 16 | Default SType = 1 << iota 17 | Constants 18 | RawData 19 | Jump 20 | Branch 21 | ) 22 | 23 | type Symbol struct { 24 | Stmt *Stmt 25 | Type SType 26 | Addr uint16 27 | Size uint8 28 | Operand int32 29 | } 30 | 31 | func (s *Symbol) String() string { 32 | if isRelative(s.Stmt.Mnemonic) { 33 | return fmt.Sprintf("%s T:%s A:%04X O:%d", s.Stmt, s.Type.String(), s.Addr, s.Operand) 34 | } else { 35 | return fmt.Sprintf("%s T:%s A:%04X O:0x%04X", s.Stmt, s.Type.String(), s.Addr, uint(s.Operand)) 36 | } 37 | } 38 | 39 | func (s *Symbol) Data() (d []byte, err error) { 40 | op := s.Stmt.Mnemonic 41 | d = make([]byte, s.Size) 42 | var sum int 43 | switch op { 44 | case HEX: 45 | for i := 0; i < len(s.Stmt.Expr); i += 2 { 46 | n, err := ehex.Decode(d[sum:], s.Stmt.Expr[i].Value) 47 | if err != nil { 48 | return nil, fmt.Errorf("invalid data%s[%d] error:%s", s, s.Size, err) 49 | } 50 | sum += n 51 | } 52 | 53 | if sum != int(s.Size) { 54 | err = fmt.Errorf("invalid data%s [%d!=%d]", s, s.Size, sum) 55 | return 56 | } 57 | case STR: 58 | d[0] = uint8(s.Size) 59 | sum += 1 60 | fallthrough 61 | case ASC: 62 | for i := 0; i < len(s.Stmt.Expr); i += 2 { 63 | sum += copy(d[sum:], s.Stmt.Expr[i].Value) 64 | } 65 | if sum != int(s.Size) { 66 | err = fmt.Errorf("invalid data[%d!=%d]%s", s.Size, sum, s) 67 | } 68 | 69 | case ADR: 70 | if s.Size != 2 || s.Operand > 0xffff || s.Operand < 0 { 71 | err = fmt.Errorf("invalid data%s[%d]", s, s.Size) 72 | return 73 | } 74 | binary.LittleEndian.PutUint16(d, uint16(s.Operand)) 75 | default: 76 | ins := ins.GetNameTable(op.String(), s.Stmt.Mode.String()) 77 | d[0] = ins.Op 78 | switch ins.Bytes { 79 | case 1: 80 | case 2: 81 | d[1] = uint8(s.Operand) 82 | case 3: 83 | binary.LittleEndian.PutUint16(d[1:], uint16(s.Operand)) 84 | default: 85 | 86 | err = fmt.Errorf("invalid ins:%s", s.Stmt) 87 | } 88 | } 89 | return 90 | } 91 | 92 | type Compiler struct { 93 | stmt []*Stmt 94 | defined map[string]*Stmt 95 | Symbols []*Symbol 96 | 97 | errCount int 98 | warnAsError bool 99 | } 100 | 101 | func (c *Compiler) errorf(f string, args ...interface{}) { 102 | c.errCount++ 103 | if c.errCount > 10 { 104 | log.Fatal("too many errors") 105 | } 106 | log.Printf(f, args...) 107 | } 108 | 109 | func (c *Compiler) warnf(f string, args ...interface{}) { 110 | if c.warnAsError { 111 | c.errorf(f, args...) 112 | return 113 | } 114 | log.Printf(f, args) 115 | } 116 | 117 | func (c *Compiler) preprocess(s *Stmt) { 118 | if s.Mnemonic == 0 { 119 | return 120 | } 121 | 122 | if s.Label != "" && !isLocalLabel(s.Label) { 123 | if _, ok := c.defined[s.Label]; ok { 124 | c.errorf("duplicate %s ", s.Label) 125 | return 126 | } 127 | c.defined[s.Label] = s 128 | } 129 | 130 | if isRelative(s.Mnemonic) { 131 | s.Mode = ins.Relative 132 | } 133 | } 134 | 135 | func (c *Compiler) expandConst() (change bool) { 136 | for _, s := range c.stmt { 137 | ne := Expression{} 138 | for _, t := range s.Expr { 139 | if t.Type != TLabel { 140 | ne = append(ne, t) 141 | continue 142 | } 143 | ns, ok := c.defined[string(t.Value)] 144 | if !ok { 145 | // might be in link time 146 | ne = append(ne, t) 147 | continue 148 | } 149 | switch ns.Mnemonic { 150 | case EPZ: 151 | switch s.Mode { 152 | case ins.Absolute: 153 | s.Mode = ins.ZeroPage 154 | case ins.AbsoluteX: 155 | s.Mode = ins.ZeroPageX 156 | case ins.AbsoluteY: 157 | s.Mode = ins.ZeroPageY 158 | } 159 | fallthrough 160 | case EQU: 161 | change = true 162 | ne = append(ne, ns.Expr...) 163 | default: 164 | ne = append(ne, t) 165 | } 166 | } 167 | s.Expr = ne 168 | } 169 | return 170 | } 171 | 172 | func isLocalLabel(s string) bool { 173 | if s == "" { 174 | return false 175 | } 176 | return s[0] == '^' 177 | } 178 | 179 | func Compile(sl []*Stmt, of io.Writer, zobj, debug bool) (err error) { 180 | 181 | rl := []*Stmt{} 182 | for _, s := range sl { 183 | if s.Mnemonic != 0 { 184 | rl = append(rl, s) 185 | } 186 | } 187 | 188 | c := &Compiler{stmt: rl, defined: map[string]*Stmt{}} 189 | for _, s := range c.stmt { 190 | c.preprocess(s) 191 | } 192 | 193 | // Find any available value at compile time 194 | for c.expandConst() { 195 | } 196 | c.initSymbol() 197 | c.determineType() 198 | c.determineSize() 199 | 200 | // split into blocks 201 | if debug { 202 | for _, sym := range c.Symbols { 203 | fmt.Println(sym) 204 | } 205 | } 206 | 207 | if c.errCount > 0 { 208 | return fmt.Errorf("compile failed") 209 | } 210 | err = NewObjFile(c.Symbols).WriteTo(of) 211 | if err != nil { 212 | log.Fatalf("compile failed:%s", err) 213 | } 214 | return 215 | } 216 | 217 | func (c *Compiler) initSymbol() { 218 | for _, stmt := range c.stmt { 219 | c.Symbols = append(c.Symbols, &Symbol{ 220 | Stmt: stmt, 221 | }) 222 | } 223 | } 224 | 225 | func (c *Compiler) determineType() { 226 | for _, sym := range c.Symbols { 227 | switch sym.Stmt.Mnemonic { 228 | case EQU, EPZ: 229 | sym.Type = Constants 230 | continue 231 | case JSR, JMP: 232 | sym.Type = Jump 233 | continue 234 | } 235 | 236 | if isRawData(sym.Stmt.Mnemonic) { 237 | sym.Type = RawData 238 | continue 239 | } 240 | if sym.Stmt.Mode == ins.Relative { 241 | sym.Type = Branch 242 | continue 243 | } 244 | 245 | sym.Type = Default 246 | } 247 | } 248 | 249 | func (c *Compiler) determineSize() { 250 | 251 | for _, sym := range c.Symbols { 252 | if IsNonAddress(sym.Stmt.Mnemonic) { 253 | continue 254 | } 255 | 256 | if isRawData(sym.Stmt.Mnemonic) { 257 | for _, t := range sym.Stmt.Expr { 258 | if t.Type == TOperator { 259 | continue 260 | } 261 | ld := uint8(len(t.Value)) 262 | switch sym.Stmt.Mnemonic { 263 | case STR, ASC: 264 | sym.Size += ld 265 | case HEX: 266 | if ld%2 != 0 { 267 | ld += 1 268 | } 269 | sym.Size += ld / 2 270 | case ADR: 271 | sym.Size += 2 272 | default: 273 | c.errorf("%d unsupported raw data %s", sym.Stmt.Line, sym.Stmt) 274 | } 275 | } 276 | if sym.Stmt.Mnemonic == STR { 277 | sym.Size += 1 278 | } 279 | 280 | continue 281 | } 282 | 283 | switch sym.Stmt.Mnemonic { 284 | case LSR, ASL: 285 | sym.Stmt.Mode = ins.Accumulator 286 | } 287 | 288 | i := ins.GetNameTable(sym.Stmt.Mnemonic.String(), sym.Stmt.Mode.String()) 289 | if i.Bytes < 1 || i.Bytes > 3 { 290 | c.errorf("unsupported bytes length:%d sym:%s mod:%s", i.Bytes, sym.Stmt.Mnemonic, sym.Stmt.Mode) 291 | } 292 | sym.Size = i.Bytes 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /lisa/decode.go: -------------------------------------------------------------------------------- 1 | package lisa 2 | 3 | import ( 4 | "fmt" 5 | "go6502/ins" 6 | ) 7 | 8 | func Disasm(d []byte) (sl []*Stmt, err error) { 9 | for len(d) > 0 { 10 | op := d[0] 11 | in := ins.Table[op] 12 | if in.Bytes == 0 { 13 | err = fmt.Errorf("Invalid op") 14 | return 15 | } 16 | s := &Stmt{Line: 0, Mnemonic: mnemonicMap[in.Name.String()]} 17 | 18 | switch in.Mode { 19 | case ins.Relative: 20 | s.Oper = fmt.Sprintf("*!%+d", int8(d[1])+2) 21 | case ins.ZeroPage, ins.Immediate: 22 | s.Oper = fmt.Sprintf("$%02X", d[1]) 23 | case ins.ZeroPageX: 24 | s.Oper = fmt.Sprintf("$%02X,X", d[1]) 25 | case ins.ZeroPageY: 26 | s.Oper = fmt.Sprintf("$%02X,Y", d[1]) 27 | case ins.Absolute: 28 | s.Oper = fmt.Sprintf("$%02X%02X", d[2], d[1]) 29 | case ins.AbsoluteX: 30 | s.Oper = fmt.Sprintf("$%02X%02X,X", d[2], d[1]) 31 | case ins.AbsoluteY: 32 | s.Oper = fmt.Sprintf("$%02X%02X,Y", d[2], d[1]) 33 | case ins.Indirect: 34 | s.Oper = fmt.Sprintf("($%02X%02X)", d[2], d[1]) 35 | case ins.IndirectX: 36 | s.Oper = fmt.Sprintf("($%02X),X", d[1]) 37 | case ins.IndirectY: 38 | s.Oper = fmt.Sprintf("($%02X),Y", d[1]) 39 | default: 40 | } 41 | d = d[in.Bytes:] 42 | sl = append(sl, s) 43 | } 44 | 45 | return 46 | } 47 | -------------------------------------------------------------------------------- /lisa/doc/Lisa_Assembler_1A.txt: -------------------------------------------------------------------------------- 1 | 2 | ======================================= 3 | LISA 2.5 DOCUMENTATION FILE #1 4 | ======================================= 5 | 6 | WRITTEN BY: DATA CAT 7 | 8 | 9 | 10 | A PROFESSIONAL ASSEMBLY LANGUAGE DEVELOPMENT SYSTEM FOR APPLE 11 | COMPUTERS 12 | LISA 2.5 13 | 14 | -What is Lisa 15 | LISA (pronounced LI ZA, not LE SA) is an interactive 6502 assembler 16 | for the Apple II. Due to its structure, code compression, 17 | interaction, and built-in features, LISA is easily the most powerful 18 | assembler available for the Apple II. With LISA, machine language 19 | programming becomes almost as easy as BASIC. LISA works with you 20 | instead of working against you. LISA is a symbolic assembler, the 21 | programmer does not have to keep track of addresses as with the 22 | built-in ROM mini-assembler. More pseudo opcodes, Sweet 16 mnemonics 23 | (which turns your Apple II into a 16-bit machine), more extended 24 | mnemonics and more commands which allow the flexible use of DOS 3.2. 25 | 26 | LISA also works with the new Apple II PLUS as well as with Apple's 27 | Autostart ROM or the language system. If your Apple II has the Lazer 28 | MicroSystems Lower Case +Plus installed, you may enter and display the 29 | entire 96 upper/lower case ASCII character set and all characters may 30 | be entered directly from an unmodified Apple keyboard. Not only that, 31 | but should you desire to incorporate lower case input into your 32 | assembly language programs Lazer Systems has provided a source listing 33 | of the "LIZA P2.L" routines (used by LISA) for your convenience. 34 | 35 | -Requirements 36 | 37 | Requirements include at least one drive and 48K bytes of RAM. LISA 38 | 2.5 64K requires a language card for proper operation. 39 | 40 | -Disk Facilities Provided 41 | 42 | LISA uses several disk options. You may save LISA text files to disk 43 | as either a text or "LISA" type file. "LISA" files are much faster 44 | and require less space on the disk, but are incompatible with the rest 45 | of the world. Text files may be read in by APPLE Pie or your BASIC 46 | programs but are much slower than the "LISA" type files for simple 47 | loading and saving. In addition a LISA source file on disk may be 48 | appended to the existing file in memory by using the "AP(PEND)" 49 | command. During assembly it is possible to "chain" source files in 50 | from the disk using the "ICL" pseudo opcode. This allows the user to 51 | assemble text files which are much larger than the available memory in 52 | the Apple II. Likewise, by using the "DCM" pseudo opcode, it is 53 | possible to save generated code on to the disk, so codefiles of almost 54 | any length may be generated. 55 | 56 | -How does LISA interface with the Monitor & DOS? 57 | 58 | LISA operates under DOS 3.2 for file maintenance and peripheral 59 | control. Any DOS command may be executed directly from LISA's command 60 | level. Since PR# & IN# are DOS commands, PR# & IN# are available for 61 | peripheral control. In addition, control-P is reserved for use with 62 | user defined routines. These routines may be printer drivers for use 63 | with I/O devices not utilizing an on-board ROM, or use with device 64 | drivers using the game I/O jack, or any user defined utility such as 65 | slow list, entry into BASIC, etc. LISA uses only standard routines in 66 | the Apple Monitor, so LISA will work with both the normal Apple 67 | monitor and the Autostart ROM. LISA modifies pointers in DOS 3.2, 68 | therefore, when your LISA disk is booted the DOS which is loaded into 69 | memory should not be used for BASIC, or TINY PASCAL programs. LISA 70 | save source files in a special "LISA" format. When you catalog the 71 | disk these files will have filetype of "L". When running under an 72 | unmodified DOS these files will look like binary files, but they 73 | cannot be BLOADED or BRUN'd. LISA is provided on DOS 3.2 but may be 74 | converted to DOS 3.3 using the DOS 3.3 MUFFIN program. 75 | 76 | -Important Concepts 77 | 78 | 1) SOURCE FORMAT 79 | 2) LABEL FIELD 80 | 3) MNEMONIC FIELD 81 | 82 | a) STANDARD MNEMONICS 83 | b) EXTENDED MNEMONICS 84 | c) SWEET-16 MNEMONICS 85 | d) PSEUDO OPCODES 86 | 87 | 4) OPERAND FIELD 88 | 5) COMMENT FIELD -Assembly Language Source Format 89 | 90 | Source statements in LISA are entered in a "free format" fashion. 91 | Source statements in LISA use the following format: 92 | 93 | LABEL MNEMONIC OPERAND ;COMMENT 94 | 95 | Each member of the source statement is called a "field". There is a 96 | LABEL field, a MNEMONIC field, an OPERAND field, and a COMMENT field. 97 | These fields may or may not be optional depending upon the context of 98 | the statement. These fields must be separated by at least one blank, 99 | & interleaving blanks may not appear inside any of the fields. If an 100 | upper case alphabetic character appears in column one, then that 101 | character defines the beginning of the LABEL field. If column one is 102 | blank, then this is a signal to LISA that there will not be a label on 103 | the current line. If column one contains a semicolon (";") or an 104 | asterisk ("*"), then the rest of the line will be considered a comment 105 | and will be ignored. The appearance of any other character in column 106 | one constitutes an error and this error will be flagged at edit time 107 | (assuming that you're using LISA's built-in editor). 108 | 109 | -THE LABEL FIELD 110 | 111 | The label field contains a one to eight character label whose first 112 | character begins in column one. If you attempt to place a label in 113 | any column except column one LISA will mistake the label for a 6502 114 | mnemonic and will (more than likely) give you a syntax error. Valid 115 | characters in labels are the uppercase alphabetics, numerics, and the 116 | two special characters period (".") and underline ("-"). While LISA 117 | 2.5 will accept certain other characters within a filename, they 118 | should be avoided to insure upwards compatibility with upcoming 119 | versions of LISA. Lower case alphabetics will be converted to 120 | uppercase when processing labels, they may be used if convenient. 121 | 122 | Labels are terminated with either a blank or a colon. If a blank 123 | terminates the label that a 6502 mnemonic must appear after the 124 | label. If a colon terminates the line than the remainder of the line 125 | is ignored and the label will appear on the line by itself. 126 | 127 | A special type of label, local labels, will be discussed later in this 128 | manual. 129 | 130 | -THE MNEMONIC FIELD 131 | 132 | This field, delimited by a blank, must contain the three character 133 | mnemonic. This may be any of the valid 6502 mnemonics, Sweet-16 134 | mnemonics, or pseudo-opcodes. 135 | 136 | VALID MNEMONICS: 137 | 138 | ADC AND ASL BCC BCS BEQ BIT BMI BNE BPL 139 | BRK BVC BVS CLC CLD CLI CMP CPX CPY DEC 140 | DEX DEY EOR INC INX INY JMP JSR LDA LDX 141 | LDY LSR NOP ORA PHA PHP PLA PLP ROL ROR 142 | RTI RTS SBC SEC SED SEI STA STX STY TAX 143 | TAY TSX TXA TXS TYA 144 | 145 | EXTENDED MNEMONICS: 146 | 147 | BTR BFL BGE BLT XOR 148 | 149 | SWEET-16 MNEMONICS: 150 | 151 | SET LDR STO LDD STD POP STP ADD SUB PPD 152 | CPR INR DCR RTN BRA BNC BIC BIP BIM BNZ 153 | BMI BNM BKS RSB BSB BNZ 154 | 155 | PSEUDO OPCODES: 156 | 157 | OBJ ORG EPZ EQU ASC STR HEX LST NLS DCM 158 | ICL END ADR DCI INV BLK DFS PAG PAU BYT 159 | HBY DBY LET TTL NOG GEN PHS DPH .DA .IF 160 | .EL .FI USR 161 | 162 | LISA mnemonics may be entered in either uppercase or lowercase, LISA 163 | will always convert the input mnemonic to upper case.A complete 164 | description of these appear in the following sections. 165 | 166 | -THE OPERAND FIELD 167 | 168 | The operand field, again delimited by a blank, 169 | contains the address expression and any required addressing mode 170 | information. 171 | 172 | -THE COMMENT FIELD 173 | 174 | Following the operand field comes the optional comment field. The 175 | comment field must begin with a semicolon (";") and must be separated 176 | from the operand field by at least one blank. The remainder of the 177 | line (up to return) will be ignored by LISA. If there is no operand 178 | field (e.g. implied or accumulator addressing mode) then the comment 179 | field may follow the mnemonic field. Comments may not appear on the 180 | same line as the "END", "LST", PAG, PAU and "NLS" pseudo opcodes. As 181 | previously mentioned, comments may appear on line by themselves by 182 | placing a semicolon or an asterisk in column one. 183 | 184 | -ADDRESSING MODES 185 | 186 | 1) ADDRESS EXPRESSIONS 5) INDEXED BY X ADDRESSING 187 | 2) IMMEDIATE ADDRESSING MODE 6) INDEXED BY Y ADDRESSING 188 | a) Standard syntax 7) RELATIVE ADDRESSING 189 | b) Low Order Byte Selection 8) IMPLIED ADDRESSING 190 | c) High Order Byte Selection 9) INDIRECT, INDEXED BY Y 191 | d) Extended Modes 10) INDEXED BY X, INDIRECT ADDRESSING 192 | 3) ACCUMULATOR ADDRESSING MODE 11) INDIRECT ADDRESSING 193 | 4) ABSOLUTE/ZERO PAGE ADDRESSING 12) LOCAL LABELS 194 | 195 | -ADDRESS EXPRESSIONS 196 | 197 | The operand filed provides two pieces of information to LISA. It 198 | provides the addressing mode, which tells the computer how to get the 199 | data, and the address expression which tells the computer where the 200 | data is coming from. An address expression is simply an integer 201 | expression, much like the expressions found in Integer BASIC, whose 202 | result is a sixteen-bit unsigned integer in the range 0-65535. 203 | Version 2.5 supports addition, subtraction, multiplication, division, 204 | logical-AND, logical-OR, logical-exclusive OR, equality, and 205 | inequality. 206 | 207 | An address expression can be defined in the following terms: 208 | 1) An address expression is defined as a "term" optionally 209 | followed by an operator and another address expression. 210 | 2) An operator is either "+", "-", "*", "/", "&", "|", "^", 211 | "=", or "#". 212 | 3) A term is either a label (regular or local), a hex 213 | constant, a decimal constant, a binary constant, a character constant 214 | or the special symbol"*". 215 | 4) Hex constants may be in the range $0000- $FFFF and must 216 | begin with the symbol "$". 217 | 5) Decimal constants may be in the range 0 - 65535 and may 218 | begin with the symbol "!" (the "!" is optional). Note that decimal 219 | constants in the range 65536- 99999 (i.e. overflow) will not be 220 | detected at edit time or assembly time, please be careful! Signed 221 | decimal constants (i.e. negative decimal values) must begin with the 222 | sequence "!-". 223 | 6) Binary constants may be in the range %0000000000000000 - 224 | %1111111111111111 and must begin with the special symbol "%". 225 | 7) Character constants come in two varieties. If you wish to 226 | use the standard ASCII representation (i.e. high order bit of) simply 227 | enter the character enclosed by two apostrophes (e.g. 'A'). To use 228 | the extended ASCII form (i.e. high order bit on) enclose the 229 | character in quotes (e.g. "A"). 230 | 8) The special symbol "*" can be thought of as a function 231 | which returns the address of the beginning of the current source 232 | line. 233 | 234 | Address expressions may not contain any interleaving blanks. 235 | 236 | Example of Address Expression 237 | 238 | LBL+$3 HERE-THERE *+!10 "Z"+$1 $FF !10 !-936 239 | LABEL/2*X^$FFFF&$10FF|1 LBL-$FF+!10-%1010011 240 | 241 | Address expressions are evaluated from RIGHT TO LEFT! This is very 242 | similar in operation to the expression analyzer used by the APL 243 | programming language. Parenthesis are not allowed. 244 | 245 | Example: 246 | 247 | $5+$3 evaluates to $8 248 | $5+$3-$2 " to $6 249 | $5-$3+$2 " to $0 ($3+$2 = $5 which is subtracted from $5) 250 | 251 | 252 | In 99% of the cases, the order of evaluation will not make any 253 | difference since address expressions seldom have more than two terms. 254 | The only time the right to left evaluation sequence will make a 255 | difference is when the address expression contains more that two terms 256 | and the subtraction operator is used. From this point on, whenever 257 | "" appears you may substitute any valid address 258 | expression. A very special type of address expression is the "zero 259 | page address expression". In a nutshell, a zero page address 260 | expression is one which results in a value less than or equal to $FF 261 | and does not contain any terms greater that $FF. For example, 262 | although $FE+$1 is a valid zero page address expression, $100-$1 is 263 | not. This is because the expression contains a term greater than $FF 264 | ($100). Also, if during evaluation the expression ever evaluates to a 265 | value greater than $FF, the expression will not be a zero page 266 | expression, Naturally, if an expression evaluates to a value greater 267 | that $FF,even though its terms are all less than $FF, it will not be a 268 | zero page expression. 269 | 270 | Multiplication, division, logical-AND, logical-inclusive OR, and 271 | logical-exclusive OR, equality, and inequality operations are also 272 | supported in LISA 2.5 address expressions. The symbols used for these 273 | operations are "*", "/", "&", "|", "^", and "#" respectively. Note 274 | that the "|" character is obtained by typing esc-1 and is displayed 275 | properly only if the user has installed a Lazer MicroSystems Lower 276 | Case +Plus. The use of the asterisk ("*") becomes very context 277 | dependent. If it is found between two expression, then the 278 | multiplication operation is assumed. If it is found in place of an 279 | expression, the current location counter value will be substituted in 280 | its place. 281 | 282 | -IMMEDIATE ADDRESSING MODE 283 | 284 | Immediate data (i.e. a constant) is preceded by a '#' or '/'. Since 285 | the 6502 is an eight bit processor, and address expressions are 286 | 16-bits long, there must be some method of choosing the high order 287 | byte or the low order byte. 288 | 289 | #: When an address expression is preceded by a "#" this instructs 290 | LISA to use the low order byte of the 16-bit address which follows. 291 | SYNTAX: # 292 | 293 | Examples: 294 | #LABEL #$FF #!6253 #%1011001 #'A' #"Z"+$1 295 | 296 | 297 | /: When the address expression is preceded by a "/" this instructs 298 | LISA to use the high order byte of the 16-bit address which follows. 299 | SYNTAX: / 300 | 301 | Examples: 302 | /LABEL /$FF /!6253 /%101001100 /LBL+$4 /$F88F 303 | 304 | Note: "/" is one of the exceptions to MOS syntax. MOS uses "#<" 305 | instead. We feel the "/" is easier to type into the system (it saves 306 | you having to type two shifted characters). Another reason for not 307 | using the ">" and "<" operators will become evident when discussing 308 | local labels. 309 | 310 | In addition to the standard syntax, LISA provides the user with three 311 | very convenient extensions to the immediate addressing mode. 312 | 313 | A single apostrophe followed by a single character will tell LISA to 314 | use the ASCII code (high order bit off) for that character as the 315 | immediate data. This is identical to "'' except you do not 316 | have to type the "#" and closing apostrophe. 317 | SYNTAX: ' 318 | 319 | The quote can be used in a similar manner to the apostrophe, except 320 | the immediate data used will then be in the extended ASCII format 321 | (high order bit on). 322 | SYNTAX: " 323 | 324 | Examples: 325 | 'A -SAME AS #'A' 'B -SAME AS #'B' '% - SAME AS #'%' "C - SAME 326 | AS #"C" "D - SAME AS #"D" "# - SAME AS #"#" 327 | 328 | If you're wondering why you would want to use the #"A" version, 329 | remember that an address expression is allowed after the "#". This 330 | allows you to construct constants of the form #"Z"+$1 which is useful 331 | on occasion. Address expressions are not allowed after the " or ' in 332 | extended form. 333 | 334 | The last extension to the immediate mode concerns hexadecimal 335 | constants. Since hex constants are used much more often than any 336 | other data type in the immediate mode, a special provision has been 337 | made for entering them. If the first character of the operand field 338 | is a DECIMAL digit ("0"-"9") then the computer will interpret the 339 | following digits as immediate HEXADECIMAL data. If you need to use a 340 | hexadecimal number in the range $A0-$FF you must precede the 341 | hexadecimal number with a decimal zero.This is required so that LISA 342 | will not mistake your hexadecimal number for a label. 343 | 344 | 345 | 346 | Examples: 347 | 00 -SAME AS #$0 05 -SAME AS #$5 10 -SAME AS #$10 OFF -SAME 348 | AS #$FF 349 | WARNING** These special forms of the immediate addressing mode were 350 | included to provide compatibility with an older assembler. Since 351 | LISA's introduction, the assembler using this special syntax has been 352 | removed from the marketplace. To help streamline future versions of 353 | LISA these syntax additions will not be present in future versions of 354 | LISA. They are included in LISA 2.5 only for purposes of 355 | compatibility with older versions of LISA. DON'T USE THESE FORMS IN 356 | NEW PROGRAMS YOU WRITE, or someday........... 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | -------------------------------------------------------------------------------- /lisa/doc/Lisa_Assembler_1B.txt: -------------------------------------------------------------------------------- 1 | 2 | -ACCUMULATOR ADDRESSING MODE 3 | 4 | The accumulator addressing mode applies to the four instructions: 5 | ASL,LSR,ROL, & ROR. Standard MOS syntax dictates that for the 6 | accumulator addressing mode you must place an "A" in the operand 7 | field. LISA is compatible with the mini-assembler built into the 8 | Apple and as such the "A" in the operand filed is not required. 9 | 10 | Examples of the Accumulator Addressing Mode: 11 | ASL ROL LSR ROR 12 | 13 | -ABSOLUTE/ZERO PAGE ADDRESSING 14 | 15 | To use the absolute/zero page addressing mode simply follow the 16 | instruction with an address expression in the operand field. LISA 17 | handles zero page addressing automatically for you ( but see EQU/EPZ 18 | description). 19 | 20 | Examples: 21 | LDA LABEL -SYMBOLIC LABEL USED LDA LABEL+$1 -LABEL PLUS 22 | OFFSET 23 | LDA $1 -NON-SYMBOLIC ZERO PAGE LDA $800 -NON-SYMBOLIC 24 | ABSOLUTE 25 | ASL LBL -SYMBOLIC LABEL ROL %10110 -NON-SYMBOLIC ZERO PAGE 26 | 27 | 28 | -INDEXED BY X ADDRESSING 29 | 30 | LISA supports the standard "indexed by X" syntax. To use this 31 | addressing mode, your operand field should have the form: 32 | ,X 33 | 34 | When LISA encounters an operand of this form, the indexed by X 35 | addressing mode will be used. If it is possible to use the zero page 36 | indexed by X addressing mode, LISA will do so. 37 | 38 | Note: STY ,X must be a zero page expression 39 | or an assembly time error will result. 40 | 41 | Examples: 42 | LDA LBL,X LDA LBL+$1,X LDA $100,X LDA$1010,X 43 | 44 | -INDEXED BY Y ADDRESSING 45 | 46 | LISA supports the standard "indexed by Y" syntax. To use this 47 | addressing mode your operand should be of the form: 48 | ,Y 49 | 50 | When LISA encounters an operand of this form, the indexed by Y 51 | addressing mode will be used. If it is possible to use the zero page 52 | addressing mode (only with LDX & STX) then the zero page version will 53 | be used. 54 | 55 | Note: STX ,Y must be a zero page expression 56 | or an assembly time error will result. 57 | 58 | Examples: 59 | LDA LBL,Y STA LBL+$80,Y LDX $0,Y 60 | 61 | 62 | -RELATIVE ADDRESSING 63 | 64 | Relative addressing is used solely by the branch instructions. 65 | Relative addressing is sytactically identical to the absolute/zero 66 | page addressing mode. 67 | 68 | Examples: 69 | BNE LBL BCS LBL+$3 BVC *+$5 BMI $900 BEQ LBL-$3 70 | 71 | 72 | 73 | -IMPLIED ADDRESSING 74 | Several mnemonics do not require any operands. When one of these 75 | instructions is used, simply leave the operand field blank. 76 | 77 | Examples: 78 | CLC SED PHA PLP 79 | 80 | -INDIRECT, INDEXED BY Y ADDRESSING 81 | 82 | Indirect, indexed by Y addressing has the following syntax: 83 | 84 | ( ),Y must be a zero page 85 | expression or an assembly time error will result. 86 | 87 | Examples: 88 | LDA (LBL),Y LDA (LBL+$2),Y LDA ($2),Y LDA (!10+%101),Y 89 | 90 | -INDEXED BY X, INDIRECT ADDRESSING 91 | 92 | The indexed by X, indirect addressing mode has the format: 93 | 94 | ( ,X) must be a zero page expression 95 | or an assembly time error will result. 96 | 97 | Examples: 98 | LDA (LBL,X) ADC (LBL+$3,X) STA (LABEL-!2,X) AND ($00,X) 99 | 100 | -INDIRECT ADDRESSING 101 | 102 | The indirect addressing mode can only be used with the JMP 103 | instruction. The indirect addressing mode uses the following syntax: 104 | 105 | ( ) may be any valid 16-bit 106 | quantity. 107 | 108 | Examples: 109 | JMP (LBL) JMP (LBL+$3) JMP ($800) 110 | 111 | -LOCAL LABELS 112 | 113 | LISA 2.5 supports a special type of label known as the local label. A 114 | local label definition consists of the up-arrow ("^") in column one 115 | followed by a digit in the range 0-9. 116 | 117 | Examples: 118 | ^0 LDA #0 ^9 STA LBL ^7 BIT $C010 119 | 120 | Local labels' main attribute is that they may be repeated throughout 121 | the text file. That is, the local label '^1' may appear in several 122 | places within the text file. To reference a local label, simply use 123 | the greater than sign ('>') or the less than sign ('<') followed by 124 | the digit of the local label you wish to access. If the less than 125 | sign is used, then LISA will use the appropriate local label found 126 | while searching backwards in the textfile. If the greater than sign 127 | is used then LISA will use the first appropriate local label found 128 | searching forward in the text file. 129 | 130 | Examples: Incrementing a 16-bit value: INC16 INC ZPGVAR 131 | BNE >1 132 | INC ZPGVAR+1 133 | ^1 RTS A Loop: 134 | LDX #0 135 | ^8 LDA #0 136 | STA LBL,X 137 | INX 138 | BNE <8 Local labels may not be equated using the EQU, 139 | "=", or EPZ pseudo opcodes. They are only allowed to appear as a 140 | statement label. 141 | 142 | USING LISA-- 143 | 144 | -GETTING LISA UP AND RUNNING To run LISA simply boot the disk. When 145 | LISA is ready to execute a command you will be greeted with a "!" 146 | prompt (the same one used by the mini:-assembler). You can also run 147 | LISA by issuing the DOS command "BRUN MXFLS". If LISA is already in 148 | memory, you can enter LISA by issuing the Apple monitor command 149 | "E000G" or control-B. This enters LISA and clears the text file in 150 | memory. If you wish to enter LISA without clearing the existing text 151 | file memory space ( a "warmstart" operation) use the "E003G" monitor 152 | command or control-C. Note: See warning and extraneous notes for the 153 | warmstart procedure. 154 | 155 | -THE COMMANDS 156 | 157 | After you successfully enter LISA, the computer will be under the 158 | control of the command interpreter. This is usually referred to as 159 | the command level. When you are at the command level a "!" prompt 160 | will be displayed and the computer will be waiting (with blinking 161 | cursor) for a command. When at the command level you have several 162 | commands available to you. They are: 163 | 164 | N(EW) LO(AD) SA(VE) W(RITE) ^D(control-D) L(IST) I(NSERT) 165 | D(ELETE) M(ODIFY) ^P(control-P) 166 | A(SM) AP(PEND) LE(NGTH BRK F(IND) 167 | 168 | The optional information is enclosed in "()". As an example you only 169 | need type "LO" to perform the "LOAD" command, "I" to execute "INSERT" 170 | command, etc. 171 | 172 | EXPLANATION OF COMMANDS-- 173 | 174 | I(NSERT) {line#} 175 | Insert command, will allow user to insert assembly language 176 | source code into the source file. This command accepts text from the 177 | keyboard and inserts it before line number "line#". If "line#" is not 178 | specified, text is inserted after the last line in the text file. If 179 | the current text file is empty, then insert will begin entering text 180 | into a new text file. If a line number is specified which is larger 181 | that the number of lines in the file, text will be inserted after the 182 | last line in the text file. To terminate the insert mode type 183 | control-E as the first character of a new line. LISA uses a logical 184 | line number scheme. The first line in the text file is line number 185 | one, the second line is line number two, line three is number three & 186 | etc. Whenever you perform an insertion between two lines the line 187 | numbers are more or less "renumbered". As an example of what happens, 188 | boot LISA disk and get into the command interpreter. Once in command 189 | mode, type "I" followed by a return. LISA will respond with a line 190 | number of one and will wait for text to be entered in the system. At 191 | this point type "LBL LDA 00" followed by return. LISA will print a 192 | "2" on the video screen and await the entry of line number two. Now 193 | type " END" (note the space before the END) followed by return. LISA 194 | will respond by printing "3" on the video screen. Now press control-E 195 | followed by return to terminate text entry. LISA will return to the 196 | command level which you will verify by noticing the "!"prompt. Now 197 | type "I 2" at the command level. LISA will respond with the line 198 | number two and will once again await you text entry. DO NOT WORRY 199 | ABOUT DELETING THE PREVIOUSLY ENTERED LINE #2. Each time you enter a 200 | line LISA "pushes" the existing lines down into memory. To prove this 201 | to yourself enter " STA $00" (note the spaces) followed by return. 202 | When "3" appears prompting you to enter a new line press control-E. 203 | Now type "L" and the Apple will display: 204 | 205 | 1 LBL LDA 00 206 | 2 STA $00 207 | 3 END 208 | 209 | Notice that "END" which was previously at line #2 has become line #3 210 | after the insertion.Since the line numbers change every time an 211 | insertion is performed it's a good idea to list a section of your 212 | source every time you perform an operation on it because the line 213 | number you decide to use may have been modified by previous editing. 214 | 215 | D(ELETE) line#1{,line#2} 216 | Deletes the lines in the range specified. If only one line 217 | number is specified then only that line is deleted. If two line 218 | numbers, separated by a comma, are specified then all the lines in 219 | that particular range are deleted. 220 | 221 | Examples: 222 | DELETE 2 -DELETED LINE #2 223 | DELETE 2,6 -DELETED LINES 2-6 Note that again, as with 224 | insert, the lines are renumbered after the command to reflect their 225 | position relative to the first line. 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | -------------------------------------------------------------------------------- /lisa/doc/Lisa_Assembler_2A.txt: -------------------------------------------------------------------------------- 1 | 2 | ======================================= 3 | LISA 2.5 DOCUMENTATION FILE #2 4 | ======================================= 5 | 6 | WRITTEN BY: DATA CAT 7 | 8 | 9 | EXPL OF COMMANDS(cont) 10 | 11 | L(IST) {line#1{,line#2}} 12 | Lists the lines in the specified range. If you need to scan a section 13 | of the text file there are two options that facilitate searching for a 14 | specified line. If, during the list, you press the space bar the 15 | listing will stop until the space bar is pressed again. If the space 16 | bar is repressed the listing will continue from where it left off. If 17 | instead of pressing the space bar you press control-C then you will be 18 | returned to the command level. 19 | 20 | Examples: 21 | LIST -LISTS ENTIRE FILE 22 | LIST 2 -LISTS LINE # 2 23 | LIST 2,6 -LIST LINES 2-6 24 | 25 | L(OAD) filename 26 | The specified LISA files will be loaded in. All valid DOS binary file 27 | options except ",A" may be suffixed to the name. LISA files are 28 | usually loaded in at location $1800. 29 | 30 | Example: 31 | LOAD LZR IOS -LOADS LZR IOS FROM DISK 32 | Note: Although the command "LOAD" is begin used this does not mean the 33 | LISA used the DOS LOAD command. Internally (and before DOS has a 34 | chance to see it), "LOAD" is converted to "BLOAD" 35 | 36 | S(AVE) filename 37 | The file in memory is saved to disk under the specified name. SAVE is 38 | internally converted to "BSAVE", so all conventions,restrictions, 39 | etc., which apply to "BSAVE" may be used (You cannot, however, specify 40 | a starting address and length as LISA does this automatically and will 41 | override your specs). Files saves using the LISA S(AVE) command are 42 | saved as special "L" type files. 43 | 44 | Example: 45 | SAVE TEMP -SAVES TEXT FILE TO DISK 46 | SAVE TEMP,S6,D2 - " " " " " to specified drive. 47 | 48 | AP(PEND) filename 49 | Reads in a text file from disk and appends it to the existing text 50 | file in memory. 51 | 52 | Example: 53 | APPEND TEMP 54 | APPEND TEMP,D2 55 | 56 | ^P (control-P) 57 | When control-P is pressed LISA will jump to location $E009 and begin 58 | execution there. Currently at location $E009 is a JMP to the command 59 | processor used by LISA. You may replace this JMP with a jump to a 60 | location where your particular routine begins. The by pressing 61 | control-P (followed by return), LISA will jump to your particular 62 | routine. To return to the command level you may either execute an RTS 63 | instruction, or JMP $E003. Space has been provided for your user 64 | routine in the area $9480-$95FF. 65 | 66 | WARNING**** Use only a JMP instruction at location $E009 as LISA 67 | system jumps appear both before and after the JMP $E009. 68 | 69 | A(SM) 70 | Assembles the current text file in memory. LISA currently allows up 71 | to 512 labels in the symbol table, to change this see the appropriate 72 | appendix. During assembly if any errors are encountered LISA will 73 | respond with: 74 | 75 | A(BORT) OR C(ONTINUE)? 76 | 77 | as well as the error message. Should you wish to continue the 78 | assembly (possibly to see if any further errors exist), write down the 79 | line number of the current infraction and press "C" followed by 80 | return. If you wish to immediately exit the assembly mode to correct 81 | the error, press "A" then return. 82 | 83 | W(RITE) filename 84 | Writes the current text file to disk as a TEXT file type. This allows 85 | you to manipulate you text file with a BASIC or APPLESOFT program. In 86 | addition, TEXT type files may be read into APPLE PIE (VER. 2.0 or 87 | greater), and you can modify your text files using this very powerful 88 | text editor. The first line output when using the W(RITE) command is 89 | "INS". With "INS" as the first line in the text files you many use 90 | the DOS "EXEC" command to reload these TEXT type files back in LISA 91 | (See control-D for more) 92 | 93 | L(ENGTH) 94 | Displays the current length of the text file in memory. 95 | 96 | ^D (control-D) 97 | Allows you to execute one DOS command from LISA. 98 | 99 | ^D PR#n -turns on an output device 100 | ^D IN#n - " " " input " 101 | ^D INT -does not put you into BASIC, but rather returns 102 | you to LISA 103 | ^D EXEC filename -where file is a TEXT type file previously created 104 | by the W(RITE) command, loads into LISA the 105 | desired text file. 106 | ~D (any other DOS command) -executes that command 107 | 108 | M(ODIFY) line#1{,line#2} 109 | Performs the sequence: 110 | 111 | L(IST) line#1{,line#2} 112 | D(ELETE) line#1{,line#2} 113 | I(NSERT) line#1 114 | 115 | which allows you to effectively replace a single line or many lines. 116 | If you do not specify a line number then the entire file will be 117 | listed,you will get an ILLEGAL NUMBER error, and you will be placed in 118 | the insertion mode with the inserted text being inserted after the 119 | last line of the text file. 120 | 121 | N(EW) 122 | Clears the existing text file,you are prompted before the clear takes 123 | place. 124 | 125 | BRK 126 | Exits from LISA, enters Apple monitor 127 | 128 | F(IND) 129 | Searchs for the label specified after the find command. FIND will 130 | print the line number of all lines containing the specified label in 131 | the label field. 132 | 133 | 134 | -SCREEN EDITING 135 | 136 | To move cursor up - Control-O 137 | down - Control-L 138 | right - Control-K 139 | left - Control-J 140 | Right arrow (Ctrl-U) - copies character under cursor 141 | Left arrow (Ctrl-H) - deletes " " " 142 | 143 | Lower case 144 | Unless you have Lazer MicroSystems' Lower Case +Plus lower case 145 | letters will appear as garbage on the screen. They are lower case in 146 | memory,hence dumping to the printer with lower case capabilities you 147 | will have lower case printed. When moving the cursor over the lower 148 | case letter junk will seen on the screen,you will see a blinking or 149 | inverted upper case letter. You can use this facility to double check 150 | lower case entry if you do not have the adapter. Since the shift key 151 | does not function for input, the ESC is used as a shift key when a 152 | software shift has to be used. "Caps lock" mode, is toggled by 153 | pressing- Control-S. In upper case mode the cursor will blink, in 154 | lower case mode it will be a static inverted character. While the 155 | caps lock mode is on the ESC will not work. 156 | 157 | LOWER CASE ADAPTED SPECIAL KEYS: 158 | 159 | "|" -by pressing "!" or "1" 160 | "~" - "^" or "N" 161 | " " - "'" or "7" 162 | "{" - "(" or "8" 163 | "}" - ")" or "9" 164 | "[" - "<" or "," 165 | "]" - ">" or "." 166 | "_" - "-" 167 | "\" - "/"```` 168 | DEL which prints a funny looking box on the screen 169 | (but not on the printer) by pressing "#" or "3" 170 | 171 | 172 | THE AVAILABLE PSEUDO OPCODES: 173 | As opcodes tell the 6502 what to do, pseudo opcodes tell LISA what to 174 | do. With pseudo opcodes you may reserve data, define symbolic 175 | addresses, instruct LISA as to where the code is to be stored, access 176 | the disk, etc. The pseudo opcodes are: 177 | 178 | OBJ: OBJECT CODE ADDRESS 179 | SYNTAX: OBJ 180 | An assembler takes a source file which you create and generates an 181 | "object code" file. This file has to be stored somewhere. It is 182 | possible to store the object file to disk, however this causes 183 | assembly to proceed very slowly, because the disk is very slow 184 | compared to the computer. The object file many also be stored 185 | directly in memory thus allowing the source file to be assembled at 186 | full speed. Except in certain cases, LISA always stores the assembled 187 | program into RAM memory. Under normal circumstances (meaning you have 188 | not told LISA otherwise), programs are stored in RAM beginning at 189 | location $800 and grow towards high memory. Often, the user needs to 190 | be able to specify where the code will be stored in memory. The OBJ 191 | pseudo opcode would be used in this instance. When an OBJ pseudo 192 | opcode is encountered in the source file, LISA will begin storing the 193 | object code generated at the specified address. This allows you to 194 | assemble code at one address and store it in another. Another use of 195 | the OBJ pseudo opcode is to reuse memory in a limited memory 196 | environment. Suppose you wish to assemble a text file 10K bytes 197 | long. Unfortunately LISA does not leave you 10K free for this use 198 | (only 4K). What you do is to assemble the first 4K of code and then 199 | save this first portion of code to disk. Now, by using the OBJ you 200 | can instruct LISA to assemble the next 4K of code on top of the old 201 | code which was saved to disk. This allows a very flexible management 202 | of memory resources. Another example, is when you wish to assemble 203 | your code for an address outside the $800-$1800 range. Since LISA 204 | uses almost every byte outside of this range for one thing or another 205 | you must assemble your code within this area. Unfortunately, not all 206 | users want to be restricted to this area. Many users might wish to 207 | assemble an I/O driver into page 3 or possibly up in high memory. 208 | Regardless of where you wish the program to run, the object code 209 | generated by LISA must be stored within the range $800-$1800. Simply 210 | use the OBJ to store your code beginning at location $800 and remember 211 | to move it to it's final location (using the monitor "move" command or 212 | the DOS ",A$" option) before running it. LISA contains a special 213 | variable called the code counter. This variable points to the memory 214 | location where the next byte of object code will be stored. The OBJ 215 | will load the value contained in its operand field into the code 216 | counter (in fact that's the only operation OBJ performs). Other 217 | pseudo opcodes affect the code counter as well, they will be pointed 218 | out as they are discussed. 219 | 220 | ORG:PROGRAM ORIGIN 221 | SYNTAX: ORG 222 | 223 | When ORG is encountered LISA begins generating code for the address 224 | specified in the address expression. When you use ORG you are making 225 | a promise that you will run your program at the address specified. If 226 | you set the program origin to $300, then you must move the program to 227 | location $300 before running it. Whenever ORG is executed it 228 | automatically performs an OBJ operation as well. Thus, if you do not 229 | want the code to be stored where you have ORG'd it, you must 230 | immediately follow the ORG statement with an OBJ statement. If you do 231 | not specify a program origin in your program, the default will be 232 | $800. Multiple ORG statements may appear in your program. Their use, 233 | however, should be avoided as they tend to cause problems during the 234 | modification of a program (e.g.if you re-ORG the program at some later 235 | date those embedded ORG statements can kill you).LISA supports several 236 | opcodes that reservemem ry,so there is no real need for more than one 237 | ORG statement within a normal program. memory,so there is no real 238 | need for more than one ORG statement within a normal program. ORG 239 | evaluates the expression in the operand field and loads the calculated 240 | variable into the code counter and the LISA location counter 241 | variable. It is important to remember that ORG affects both the 242 | location counter and code counter. 243 | 244 | WARNING** Locations $800-$1800 are reserved for code storage. If you 245 | assemble outside this range possible conflicts with LISA, the source 246 | file, the symbol table, or I/O buffer areas may arise. If you need to 247 | assemble your code at an address other than in the range $800-$1800 be 248 | sure to use the OBJ pseudo opcode to prevent conflicts. 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | -------------------------------------------------------------------------------- /lisa/doc/Lisa_Assembler_2B.txt: -------------------------------------------------------------------------------- 1 | 2 | EPZ: EQUATE TO PAGE ZERO 3 | SYNTAX: LABEL EPZ 4 | 5 | The label is assigned the value of the expression and entered into the 6 | symbol table. If evaluates to a value greater than $FF 7 | then an assembly time error occurs. If any symbolic references appear 8 | in the expression then they must have been previously defined with an 9 | EPZ pseudo opcode, or an error will result.Although LISA doesn't 10 | require you to do so, it is a good practice to define all of you zero 11 | page locations used in your program before any code is generated. 12 | Zero page is used mainly to hold variables and pointers. Before 13 | wildly using up locations in zero pages, it's wise to consult your 14 | Apple manuals to make sure that there are no zero page conflicts 15 | between your program and the monitor or whatever language you are 16 | using. When a variable is defined using the EPZ pseudo opcode then 17 | zero page addressing will be used if at all possible. The label is 18 | not optional for the EPZ opcode. The EPZ opcode only supports the 19 | addition and subtraction operators in address expressions. 20 | 21 | EQU: EQUATE 22 | SYNTAX: LABEL EQU 23 | or LABEL = 24 | 25 | The 16-bit value of will be used as the address for 26 | LABEL, and it will be entered into the symbol table. Absolute 27 | addressing will always be used when using the EQU opcode, even if the 28 | expression is less than $100. may contain symbolic 29 | references (i.e. labels), but they must have been previously defined 30 | in either an EQU statement, an EPZ statement, or as a statement 31 | label. EQU may also be used to create symbolic constants. For 32 | instance: 33 | 34 | CTLD EQU $84 or HIMEM EQU $9600 or LETRA = "A" 35 | LDA #CTLD LDA #HIMEM LDA #LETRA 36 | 37 | The use of symbolic constants in your program helps improve the 38 | readability considerably. 39 | 40 | ASC: ASCII STRING DEFINITION 41 | SYNTAX: ASC 'any string' 42 | or ASC "any string" 43 | 44 | The ASC pseudo code instructs LISA to store the following text 45 | directly in memory beginning at the current location. If the 46 | apostrophe is used, then the text is stored in normal ASCII format 47 | (i.e. high order bit off). If the quotes are used, then the 48 | character string is stored in memory in an extended ASCII format 49 | (i.e. high order bit on). Sine the APPLE II computer uses the 50 | extended ASCII format, you will probably use the latter version most 51 | of the time. If the apostrophe begins the string, then the apostrophe 52 | must be used to terminate the string. Quotes may appear anywhere 53 | inside such a string with no consequence. If the quotes are used to 54 | delimit the string, then an apostrophe may be placed anywhere inside 55 | the string with no problems whatsoever. In this case the quote must 56 | be used to terminate the string. 57 | 58 | Examples: 59 | ASC 'THIS "STRING" IS OK!' 60 | ASC "SO IS THIS 'STRING'" 61 | ASC 'THIS IS 'NOT' ALLOWED' 62 | 63 | The last example is illegal because the first occurrence of the 64 | apostrophe terminates the string, leaving an illegal operand delimiter 65 | (NOT) in the operand field. Should you ever need to place an 66 | apostrophe or a quote within a string delimited by the respective 67 | character it can be accomplished by typing two of these characters 68 | together in the string. 69 | 70 | Examples: 71 | ASC "THIS IS ""HOW"" YOU DO IT!" 72 | ASC 'THIS ''WAY'' WORKS FINE ALSO' 73 | ASC '''THIS LOOKS WEIRD, 74 | BUT IT WORKS''' 75 | 76 | In the last example the created string is: 77 | 78 | 'THIS LOOKS WEIRD, 79 | BUT IT WORKS' 80 | 81 | Note: ASC is more or less obsolete, 2.5 LISA is compatible with 82 | earlier versions thats why its included, when writing new programs you 83 | should use BYT and .DA pseudo opcodes. 84 | 85 | STR: CHARACTER STRING DEFINITION 86 | SYNTAX: STR 'any string' 87 | or STR "any string" 88 | 89 | Most high level languages define a character string as a length byte 90 | followed by 0 to 255 characters. The actual number of characters 91 | following the length byte is specified in the length byte. Strings 92 | stored this way are very easy to manipulate in memory. Functions such 93 | as concatenation, substring (RIGHT$,MID$, & LEFT$), comparisons, 94 | output, etc., are accomplished much easier when the actual length of 95 | the string is known. Except by manually counting the characters up & 96 | explicitly prefacing a length byte to your string,the ASC opcode does 97 | not allow you to use this very flexible data type. The STR opcode 98 | functions identically to the ASC opcode with one minor difference, 99 | before the characters are output to memory, a length byte is output. 100 | This allows you to create strings which can be manipulated in a manner 101 | identical to that utilized in high level languages. 102 | 103 | Examples: 104 | STR 'HI' -OUTPUTS 02 48 49 105 | STR "HELLO" -OUTPUTS 05 C8 C5 CC CC CF 106 | 107 | HEX: HEXADECIMAL STRING DEFINITION 108 | 109 | The HEX pseudo opcode allows you to define hexadecimal data and/or 110 | constants for use in your program. HEX may be used for setting up 111 | data tables, initializing arrays, etc. The string of characters 112 | following the HEX are assumed to be a string of hex digits. Each pair 113 | of digits is converted to one byte and stored in the next available 114 | memory location pointed at by the location counter Since exactly two 115 | digits are required to make one byte, you must enter an even number of 116 | hexadecimal digits after the HEX pseudo opcode, or an error will 117 | result. As such, leading zeros are required in hex strings. The hex 118 | string does not have to begin with a "$" (in fact it cannot begin with 119 | a "$"!). 120 | 121 | Examples: 122 | HEX FF003425 123 | HEX AAAA8845 124 | HEX 00 125 | 126 | LST: LISTING OPTION ON 127 | 128 | LST activates the listing option. During pass three all source lines 129 | after LST will be listed onto the output device (usually the video 130 | screen). Listing will continue until the end of the program or until 131 | an NLS pseudo opcode is encountered. Note that there is an implicit 132 | "LST" at the beginning of your program, so unless otherwise specified 133 | your program will be listed from the beginning. 134 | 135 | NLS: NO LISTING/LISTING OPTION OFF 136 | 137 | NLS deactivates the listing option. When encountered in the source 138 | file, all further text until the end of the program or until an "LST" 139 | is encountered, will not be listed. LST & NLS can be used together to 140 | list small portions of a program during assembly. By placing an "NLS" 141 | at the beginning of your program, then a "LST" before the section of 142 | code you want to printed, and then an "NLS" after the text you want 143 | printed you can selectively print a portion of the text file during 144 | assembly. Neither "LST" nor "NLS" allow an operand. 145 | 146 | ADR: ADDRESS STORAGE 147 | SYNTAX: ADR [,] 148 | 149 | The ADR lets you store, in two successive bytes, the address specified 150 | in the operand field. The address is stored in the standard low 151 | order/high order format. ADR can be used to set up "jump tables" or 152 | for storing 16-bit data. ADR is particularly useful for storing 153 | decimal and binary constants since conversion to hex is performed 154 | automatically. Multiple address expressions may appear in the operand 155 | field. If additional address expressions are present, they must be 156 | separated from each other with commas. 157 | 158 | Examples: 159 | ADR LABEL ADR LABEL-$1 ADR LABEL+$3 160 | ADR LBL1,LBL2,LBL3 161 | *- ADR !10050 *- ADR %10011011000111 162 | 163 | *-Note in particular the last two examples which demonstrate how you 164 | can store decimal and binary constants in memory using the ADR. This 165 | technique is very useful for translating BASIC programs to assembly 166 | language 167 | 168 | END: END OF ASSEMBLY 169 | 170 | END tells LISA that the end of the source file has been encountered. 171 | During passes one and two LISA will start at the beginning of the text 172 | file and continue with the next pass. At the end of pass three 173 | control will be returned to LISA's command level. If the END is not 174 | present in the source file then a "MISSING END" error will occur at 175 | the end of pass one. 176 | 177 | ICL: INCLUDE TEXT FILE 178 | SYNTAX: ICL "filename" 179 | 180 | ICL is a very powerful and advanced pseudo opcode. It allows you to 181 | "chain" in another text file. This pseudo should be used when there 182 | is not enough memory available for the current text file. LISA 183 | provides you with enough memory for approximately 1500 to 2000 lines 184 | of text. Should you try to exceed this limit a "memory full" error 185 | will result. When this happens,delete the last 10 lines or so (to 186 | give you working space)and, as the last line of your text file use ICL 187 | to link in the next file. Once the ICL has been entered, save the 188 | text file to disk. Now use the N(EW) command to clear the text file 189 | workspace and then enter the rest of your assembly language text file, 190 | continuing from where you left off. Once you have finished entering 191 | the text, save the text file disk under the name specified in the ICL 192 | . Now load the original files and assemble it. During assembly LISA 193 | will automatically bring in the second file from disk and continue 194 | assembly at that point. 195 | 196 | Note** You shouldn't use "ICL" unless you really have to. The use of 197 | ICL slows down assembly from 20,000 lines per minute to about 500-1000 198 | lines per minute due to considerable disk access. 199 | Since LISA is a three pass assembler the original text file in memory 200 | must be saved to disk. It is saved under the name "TEMP." so you 201 | should be careful not to use that filename. After assembly the 202 | resident text file in memory will be the last text file chained in. 203 | The original text file is not brought back into memory. During 204 | assembly, if an error occurs in a section of code which was ICL'd off 205 | of the disk, the error message will give you the name of the file, as 206 | well as the line number within the file where the infraction 207 | occurred. Also the option of continuing or aborting. If you abort 208 | you will find the text file with the error currently in memory. You 209 | may fix the error, resave the text file to disk under its original 210 | name, then reload "TEMP." and reassemble the text file. ICL is 211 | similar to END in that it must be the last statement in the text 212 | file. Any additional lines after the ICL will be ignored. There is 213 | no limit to the number of files that you can chain together using 214 | ICL. 215 | 216 | DCM: DISK COMMAND 217 | SYNTAX: DCM "dos command" 218 | 219 | During pass one and two the DCM pseudo is ignored. During pass three, 220 | however, whatever string is placed between the quotes gets executed as 221 | an Apple DOS command. A control-D is not required at the beginning of 222 | the DOS command. The DCM has several uses, you may use it to 223 | selectively turn on and off input and output devices during assembly 224 | (using PR# & IN#), it can be used to save generated code to disk, thus 225 | freeing memory space.It can be used to create a disk text file listing 226 | of the assembled program,also it can be used to prevent the symbol 227 | table listing from being printed, and for loading load modules off of 228 | the disk after an assembly.Since LISA only allows 4K bytes for your 229 | obj code (from $800-$1800), you have to BSAVE you obj file files to 230 | disk when this 4K is used up.Once the file is BSAVEd to disk you can 231 | use the OBJ pseudo to begin storing your object code beginning at 232 | location $800 once again.When the second 4K is used up you must once 233 | again use the DCM/OBJ sequence to make room for the new object code. 234 | Once these "load modules" have been saved to disk, you can reload them 235 | in sequence and the run the finished product. However,you cannot 236 | simply BLOAD each of the object modules and expect your program to 237 | run. The BLOAD command loads the program in from where it was saved, 238 | since all load modules were saved beginning at location $800, the 239 | BLOAD command will load them in on top of each other! To get around 240 | this, use the "A$" option when BLOADing a program to load the module 241 | into its correct memory location. If fact, when BSAVEing a pgm with 242 | DCM its a good idea to make the loading address part of the file 243 | name(for example: OBJ.1/$1800) 244 | 245 | Examples: 246 | . 247 | DCM "BSAVE OBJECT/$800,A$800,L$1000 248 | DCM "BSAVE OBJECT/A$1800,A$800,L$1000" 249 | 250 | The symbol table listing may be suppressed by using the disk command 251 | "INT". This should be entered in your program immediately before the 252 | "END". Assembly automatically terminates when the DCM "INT" pseudo is 253 | encountered and you are returned to the command level. To create a 254 | disk file listing of the assembly text file use the DCM command 255 | sequence: 256 | 257 | DCM "OPEN " 258 | DCM "WRITE " 259 | 260 | Once this has been accomplished all further text normally written onto 261 | the screen will be sent to the disk under the name "". The 262 | last statement before the END (or DCM "INT" if present) should be: DCM 263 | "CLOSE". This will close the file, restore buffers, etc. Since the 264 | CLOSE will be executed before the symbol table is printed, the symbol 265 | table will not be included in your text file listing. If you need to 266 | include the text, then omit the DCM "CLOSE" and explicitly CLOSE the 267 | file with an immediately CLOSE command when you are returned to the 268 | command level. 269 | 270 | Warning** Due to the memory management techniques used (48K MAXFILES) 271 | is always set to one. This implies that several problems can develop 272 | if your program contains other disk commands sandwiched between the 273 | OPEN & CLOSE commands. Should you need to execute a disk command 274 | while writing the assembled source to disk you must first CLOSE the 275 | file. Once closed, you can execute the DOS command. After the 276 | command (DOS) is executed you may continue writing the assembly 277 | listing by APPENDing (instead of OPENing) and then WRITEing to the 278 | file. *NOTE ** Remember,any DOS command terminates the WRITE command, 279 | so if you issue any DOS commands when writing a text file out to disk 280 | you must reissue the WRITE command immediately after the DOS 281 | command.ICL uses the DOS, so care must be take when writing files to 282 | disk 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | -------------------------------------------------------------------------------- /lisa/doc/Lisa_Assembler_3A.txt: -------------------------------------------------------------------------------- 1 | 2 | ======================================= 3 | LISA 2.5 DOCUMENTATION FILE #3 4 | ======================================= 5 | 6 | WRITTEN BY: DATA CAT 7 | 8 | 9 | 10 | EXPL OF COMMANDS (cont): 11 | 12 | PAU: PAUSE/FORCE ERROR 13 | 14 | PAU is ignored during passes one and two. During pass three however, 15 | this pseudo will automatically cause an error ("**ERROR: PAUSE 16 | ENCOUNTERED") to occur. At this point the programmer may A(BORT) the 17 | assembly or C(ONTINUE) the assembly. PAU is very useful for debugging 18 | purposes as you don't have to watch the screen with your finger on the 19 | space bar should you desire to stop the assembly listing at some 20 | particular section of code. PAU is also useful in determining where 21 | the 4K cutoff is when you are saving obj files. Although an error 22 | message is generated, this has no effect on the assembly. If the 23 | pause error is the only error encountered then the assembly can be 24 | considered successful. 25 | 26 | PAG: PAGE EJECT 27 | 28 | PAG will print a control-L to the listing device when encountered 29 | during pass three. If you are sending the listing to a printer with 30 | forms control, your printer should skip to top-of-form. PAG allows 31 | you to format your listings nicely, breaking up subroutines so that 32 | they begin on different pages. 33 | 34 | DCI: DEFINE CHARACTERS IMMEDIATE 35 | SYNTAX: DCI "any string" 36 | or DCI 'any string' 37 | DCI is a special hybrid pseudo. In function its identical to ASC with 38 | one exception:The last character in the string will have an order bit 39 | which is opposite the value for the rest of the string. That is,if 40 | you are storing the string in memory with high order bit on,then the 41 | last character in the string will be stored with its high order bit 42 | equal to zero.If the string is being stored memory with the high order 43 | bit off, then the last character in the string will be stored in 44 | memory with the high order bit one. 45 | 46 | Examples: 47 | DCI "ABCDE" -GENERATES C1 C2 C3 C4 45 48 | DCI 'ABCDE' -GENERATES 41 42 43 44 C5 49 | 50 | INV: INVERTED CHARACTERS 51 | SYNTAX: INV "any string" 52 | or INV 'any string' 53 | INV takes the string which follows and outputs the characters as APPLE 54 | inverted (inverse) characters. The high order bit is always off: so 55 | whether you use the apostrophe or quote to delimit the string is of no 56 | consequence. You should realize that only the characters directly 57 | available from the Apple keyboard plus "[", "\", "_" have inverted 58 | counterparts. The lower case letters and several special characters 59 | do not have corresponding inverted counterparts, and should they 60 | appear within the INV string, garbage will be created. 61 | 62 | Examples: 63 | INV "ABCDE" -GENERATES 01 02 03 04 05 64 | INV 'ABCDE' -GENERATES 01 02 03 04 05 65 | 66 | BLK: BLINKING CHARACTERS 67 | SYNTAX: BLK "any string" 68 | or BLK 'any string' 69 | BLK is the counter to INV. Instead of generating the code for 70 | inverted characters, BLK generates code for blinking characters. All 71 | restrictions mentioned for INV apply as well to BLK (for the same 72 | reason). 73 | 74 | HBY: HIGH BYTE DATA 75 | SYNTAX: HBY {,} 76 | HBY is similar to ADR except only the high order byte of the following 77 | address expression is stored in memory. Combined with BYT its 78 | possible to break up address tables into two groups of one byte data 79 | apiece instead of the two-byte data generated by ADR. This allows a 80 | very convenient method of loading addresses when using the index 81 | registers. 82 | 83 | Examples: 84 | HBY $1234 -GENERATES $12 85 | HBY $F3 -GENERATES $00 86 | HBY LBL -GENERATES H.O. BYTE OF THE ADDRESS LBL 87 | HBY "A" -ANY ASCII DATA ALWAYS GENERATES $00 88 | HBY LBL1,LBL2,LBL3 89 | 90 | BYT: LOW BYTE DATA 91 | SYNTAX BYT {,} 92 | BYT works in a manner similar to HBY except it stores the low order 93 | address byte into memory at the current location. BYT is also useful 94 | for introducing symbolic values into your programs. For instance, $00 95 | is often used as the "end-of-string" token. You can define a constant 96 | "EOS" (for"end-of-string")and then use BYT to store the value for EOS 97 | in memory for you. This has two beneficial effects on your program. 98 | First, it makes your program easier to read since "BYT EOS" states 99 | exactly what the value is for whereas "HEX 00" is somewhat ambiguous. 100 | The second beneficial feature is the fact that should you decide to 101 | change the EOS value from zero to say ETX (ASCII end-of-text) you only 102 | need change one line. (the EQU statement which defines EOS) instead 103 | of having to go through your program and change each occurrence of 104 | "HEX 00" to "HEX 03" 105 | 106 | Examples: 107 | BYT $1234 -GENERATES $34 108 | BYT $F3 -GENERATES $F3 109 | BYT "A" -GENERATES $C1 (EXTENDED ASCII FOR "A") 110 | BYT LBL -GENERATES CODE CORRESPONDING TO LBL'S 111 | LOW ORDER ADDRESS 112 | 113 | DFS: DEFINE STORAGE 114 | SYNTAX: DFX {,} 115 | DFS reserves memory storage for variables. DFS takes the first 116 | address expression found in the operand field and adds this value to 117 | both the location counter and the code counter. This leaves a wide 118 | gap of memory open for use by arrays, variables, etc. If the second 119 | operand is not specified, then the memory space reserved is not 120 | initialized and contains garbage. The second operand in the address 121 | expression, if specified, determines the value to which memory will be 122 | initialized. The low-order byte of the second address expression will 123 | be stuffed into each byte of the storage reserved by the DFS pseudo. 124 | ** Note** This initialization is optional. If it is not explicitly 125 | required it should not be used as it slows assembly speed down 126 | considerably. If more than two expressions are specified, the 127 | remainder are ignored. 128 | 129 | Examples: 130 | LBL DFS $1 -RESERVES ONE BYTE AT LOCATION "LBL" 131 | LBL1 DFS $100 -RESERVES 256 BYTES AT LOCATION "LBL1" 132 | LBL2 DFS 300,0 -RESERVES 300 BYTES AND INITS THEM TO ZERO 133 | 134 | DBY: DOUBLE BYTE DATA 135 | SYNTAX: DBY {,} 136 | DBY is used in a manner identical to ADR except that the address data 137 | generated is stored in high order (H.O.) byte/low order (L.O.) byte 138 | order instead of the normal L.O./H.O. order. 139 | 140 | Examples: 141 | DBY $1020 -GENERATES 10 20 142 | DBY $1234 -GENERATES 12 34 143 | DBY LABEL -GENERATES (H.O. BYTE) (L.O.BYTE) 144 | DBY LBL1,LBL2,LBL3 145 | 146 | LET: LABEL REASSIGNMENT 147 | SYNTAX: LABEL LET 148 | LET allows the programmer to redefine a previously defined (non-zero 149 | page) label. This is useful for defining local labels, counter, etc. 150 | One note of caution: LET is active on passes two and three. EQU and 151 | statement label declarations are noted only during pass two. If you 152 | declare a label during pass two as a statement label or with the EQU 153 | pseudo and then subsequently redefine it with a LET pseudo, the 154 | address used during pass three is the value defined in the LET 155 | statement regardless of the EQU or statement label definition. This 156 | is due to the fact that a label defined using the LET retains that 157 | value until another LET redefinition (with the same label) comes 158 | along. Since EQU is not active during pass three and statement label 159 | values are only noted during pass two, the label will never be set to 160 | its original value. These problems are easily overcome, simply use 161 | the LET in place of the EQU in the original definition. If the 162 | original definition was a statement label then substitute "LABEL LET*" 163 | instead 164 | 165 | TTL:TITLE 166 | SYNTAX: TTL "STRING" 167 | The TTL pseudo causes an immediate page eject (via control-L/form feed 168 | character) and then prints the title specified at the to of the page. 169 | Every 65 lines a page eject is issued and the title is printed at the 170 | top of the new page. 171 | 172 | 173 | SYNTAX: .IF 174 | Conditional assembly under LISA lets you selectively assemble code for 175 | different operating environments. For example, you could have a 176 | couple of equates at the beginning of a program which specify the 177 | target Apple system.Labels such HASPRNTR, HAS64K, HASMODEM, LCPLUS, 178 | KBPLUS, ETC., can be set true or false depending upon the hardware 179 | involved.For example, LISA 48K and 64K are the same file with just one 180 | equate changed. Conditional assembly handles all the minor 181 | details.Conditional assembly uses three pseudo opcodes: '.IF', '.EL' 182 | and '.FI'. '.IF' begins a conditional assembly sequence. '.IF' is 183 | followed by an address expression. If it evaluates to true 184 | (non-zero), the the code between the '.IF' pseudo and its 185 | corresponding '.EL' or '.FI' pseudo is assembled.If the address 186 | expression evaluates to false, then the code immediately after the 187 | '.IF' pseudo-op will not get assembled 188 | 189 | SYNTAX: .EL 190 | '.EL' terminates the '.IF' code sequence and begins the alternate code 191 | sequence. The alternate code sequence is assembled only if the 192 | address expression in the operand field of the '.IF' pseudo-op 193 | evaluates to false (zero). '.EL' (and its corresponding code section) 194 | is optional and need not be present in the conditional assembly 195 | language sequence. 196 | 197 | SYNTAX: .FI 198 | '.FI' terminates the conditional assembly language sequences. It must 199 | be present whether or not there is a '.EL' pseudo-op present.All code 200 | after a '.FI' will be assembled regardless of the value in the '.IF' 201 | operand field.**Note** LISA does not supp nested IF's. If a nested IF 202 | is present, you will be given a nasty error at assembly time. All 203 | IF's must be terminated before an END or ICL ps-op is encountered or 204 | assembly will terminate. To see any example of conditional assembly, 205 | look at the "LISA P1.L' file on the LISA master disk. 206 | 207 | PHS:PHASE 208 | SYNTAX: PHS 209 | The PHS ps-op lets you assemble a section of code for a different 210 | address, yet include the code within the body of a program running at 211 | a different address. This feature lets you include a short driver 212 | that runs at location $300, for example, within a program that 213 | normally runs up at $1000. It is the responsibility of the program at 214 | $1000 to move the program down to location $300. Technically, PHS 215 | loads the location counter with the address specified in the address 216 | expression, but it does not affect the code counter at all. In 217 | essence it performs an ORG without the OBJ. the DPH must be used to 218 | terminate the PHS code sequence. 219 | 220 | DPH- DEPHASE 221 | SYNTAX: DPH 222 | DPH is used to terminate the section of code following the PHS ps-op. 223 | It loads the code counter into the location counter, restoring the 224 | damage done by the PHS ps-op. 225 | 226 | SYNTAX: .DA {,} 227 | '.DA' is another hybrid ps-op. It is a combination of the ADR,BYT,& 228 | HBY ps-op. It is particularly useful with the SPEED/ASM package's 229 | CASE statement and similar routines. 230 | 231 | Examples: 232 | LBL1 .DA #CR,RETURN 233 | LBL2 .DA 'C',#LBL,/LBL2,LBL2 234 | LBL3 .DA "HELLO THERE",#0,STRADR 235 | 236 | If an address expression is prefaced with the pound sign ("#") then 237 | the lower order byte will be used. If an address expression is 238 | prefaced with the slash ("/") then the high order byte will be used. 239 | If neither a pound sign or a slash is specified, then the two bytes of 240 | the address (in low/high format) will be stored in memory. 241 | 242 | GEN: GENERATE CODE LISTING 243 | SYNTAX: GEN 244 | GEN(and NOG)control the output during assembly.If GEN is in effect(the 245 | default)all object code output is sent to the display device 246 | 247 | NOG: NO GENERATE 248 | SYNTAX: NOG 249 | NOG will cause only the first three bytes to be listed to the output 250 | device during an assembly.This dramatically shortens progr listing 251 | containing strings and multiple address. 252 | 253 | USR: USER DEFINED PSEUDO OPCODE 254 | SYNTAX: USR 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | -------------------------------------------------------------------------------- /lisa/expr.go: -------------------------------------------------------------------------------- 1 | package lisa 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "go6502/ins" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | type TermType byte 12 | 13 | //go:generate stringer --type=TermType --trimprefix=T 14 | const ( 15 | TLabel TermType = iota + 1 16 | TOperator 17 | THex 18 | TDecimal 19 | TBinary 20 | TAscii 21 | TCurrentLine 22 | TLSLabel 23 | TGTLabel 24 | TRaw 25 | ) 26 | 27 | type Expression []*Term 28 | 29 | func (e Expression) String() string { 30 | if len(e) == 0 { 31 | return "" 32 | } 33 | var buf strings.Builder 34 | for _, t := range e { 35 | buf.WriteString(t.String()) 36 | buf.WriteByte(' ') 37 | } 38 | return buf.String() 39 | } 40 | 41 | // RIGHT -> LEFT 42 | type Term struct { 43 | Type TermType 44 | Value []byte 45 | } 46 | 47 | func (t *Term) Uint16() (u uint16, err error) { 48 | 49 | var u64 uint64 50 | switch t.Type { 51 | case THex: 52 | u64, err = strconv.ParseUint(string(t.Value), 16, 16) 53 | case TBinary: 54 | u64, err = strconv.ParseUint(string(t.Value), 2, 16) 55 | case TDecimal: 56 | u64, err = strconv.ParseUint(string(t.Value), 10, 16) 57 | default: 58 | err = fmt.Errorf("unsupported type:%s", t.Type) 59 | return 60 | } 61 | u = uint16(u64) 62 | return 63 | } 64 | 65 | func (t *Term) String() string { 66 | if t.Type == TOperator { 67 | return string(t.Value) 68 | } 69 | return fmt.Sprintf("<%s %s>", t.Type, string(t.Value)) 70 | } 71 | 72 | // Tokenize 73 | func parseOperand(b []byte) (mode ins.Mode, ae Expression, order byte, err error) { 74 | const ( 75 | direct uint16 = 1 76 | indirectFlag uint16 = 4 77 | ) 78 | if len(b) == 0 { 79 | // at least is Implied 80 | mode = ins.Implied 81 | return 82 | } 83 | 84 | if len(b) == 1 && (b[0] == 'A' || b[0] == 'a') { 85 | mode = ins.Accumulator 86 | return 87 | } 88 | 89 | // default Absolute 90 | mode = ins.Absolute 91 | 92 | t := &Term{} 93 | indirect := direct // (:2 ):4 94 | 95 | for col := 0; len(b) > 0; col += 1 { 96 | c := b[0] 97 | b = b[1:] 98 | switch c { 99 | case ' ': 100 | case ',': 101 | b = bytes.TrimSpace(b) 102 | if len(b) == 1 { 103 | switch b[0] { 104 | case 'Y', 'X': 105 | indirect |= uint16(b[0]) << 8 106 | b = b[1:] 107 | default: 108 | err = fmt.Errorf("Col:%d should end with X, Y, got=%q", col, string(b)) 109 | } 110 | continue 111 | } 112 | if len(b) == 2 && string(b) == "X)" { 113 | indirect <<= 1 114 | indirect |= uint16('X') << 8 115 | b = b[2:] 116 | continue 117 | } 118 | 119 | err = fmt.Errorf("Col:%d should end with X, X) or Y, got=%q", col, string(b)) 120 | return 121 | case OpApostrophe, OpQuote: 122 | // looking for pair 123 | i := bytes.IndexByte(b, c) 124 | if i == -1 { 125 | err = fmt.Errorf("can't find pair for=%s %s", string(c), b) 126 | return 127 | } 128 | if i <= 1 { 129 | continue 130 | } 131 | t.Type = TRaw 132 | t.Value = append(t.Value, b[:i]...) 133 | b = b[i+1:] 134 | 135 | case OpLeftBracket: 136 | // should be first 137 | if t.Type == 0 { 138 | indirect <<= 1 139 | } 140 | case OpRightBracket: 141 | // should be closed 142 | if t.Type == 0 { 143 | err = fmt.Errorf("Col:%d invalid right bracket", col) 144 | return 145 | } 146 | if indirect != 2 { 147 | err = fmt.Errorf("Col:%d no matched bracket", col) 148 | return 149 | } 150 | indirect <<= 1 151 | case OpLowOrder: 152 | if t.Type != 0 { 153 | err = fmt.Errorf("order should be the first byte of expr") 154 | return 155 | } 156 | if order != 0 { 157 | err = fmt.Errorf("duplicate order") 158 | return 159 | } 160 | order = c 161 | 162 | case OpAdd, OpMinus, OpOr, OpXor, OpAnd, OpDivision, OpAsterisk: 163 | if len(t.Value) == 0 && c == OpMinus { 164 | t.Value = append(t.Value, c) 165 | continue 166 | } 167 | 168 | if len(t.Value) == 0 && t.Type == 0 { 169 | if c == OpAsterisk { 170 | t.Value = append(t.Value, c) 171 | t.Type = TCurrentLine 172 | mode = ins.Relative 173 | continue 174 | } 175 | 176 | if c == OpDivision { 177 | if order != 0 { 178 | err = fmt.Errorf("duplicate order") 179 | return 180 | } 181 | order = OpDivision 182 | continue 183 | } 184 | } 185 | ae = append(ae, t) 186 | ae = append(ae, &Term{Type: TOperator, Value: []byte{c}}) 187 | t = &Term{} 188 | case OpHex: 189 | if t.Type != 0 { 190 | err = fmt.Errorf("duplicate type") 191 | return 192 | } 193 | t.Type = THex 194 | case OpDecimal: 195 | if t.Type != 0 { 196 | err = fmt.Errorf("duplicate type") 197 | return 198 | } 199 | t.Type = TDecimal 200 | case OpBinary: 201 | if t.Type != 0 { 202 | err = fmt.Errorf("duplicate type") 203 | return 204 | } 205 | t.Type = TBinary 206 | case OpLSLabel: 207 | if t.Type != 0 { 208 | err = fmt.Errorf("duplicate type") 209 | return 210 | } 211 | t.Type = TLSLabel 212 | case OpGTLabel: 213 | if t.Type != 0 { 214 | err = fmt.Errorf("duplicate type") 215 | return 216 | } 217 | t.Type = TGTLabel 218 | default: 219 | if t.Type == 0 { 220 | if c >= '0' && c <= '9' { 221 | t.Type = THex 222 | } else { 223 | t.Type = TLabel 224 | } 225 | } 226 | t.Value = append(t.Value, c) 227 | } 228 | } 229 | ae = append(ae, t) 230 | 231 | switch indirect { 232 | case uint16('X')<<8 | indirectFlag: 233 | mode = ins.IndirectX 234 | case uint16('Y')<<8 | indirectFlag: 235 | mode = ins.IndirectY 236 | case indirectFlag: 237 | mode = ins.Indirect 238 | case direct | uint16('X')<<8: 239 | mode <<= 1 240 | case direct | uint16('Y')<<8: 241 | mode <<= 2 242 | case direct: 243 | switch order { 244 | case '#', '/': 245 | mode = ins.Immediate 246 | case 0: 247 | default: 248 | err = fmt.Errorf("invalid order %s", string(order)) 249 | } 250 | case 2: 251 | err = fmt.Errorf("bracket not closed") 252 | } 253 | 254 | return 255 | } 256 | 257 | func syntaxCheck(expr Expression) (err error) { 258 | 259 | for i, e := range expr { 260 | switch e.Type { 261 | case THex: 262 | if len(e.Value) == 0 { 263 | return fmt.Errorf("empty hex") 264 | } 265 | for _, c := range e.Value { 266 | if bytes.IndexByte(hex, c) == -1 { 267 | return fmt.Errorf("invalid hex %x", c) 268 | } 269 | } 270 | case TDecimal: 271 | if len(e.Value) == 0 { 272 | return fmt.Errorf("empty decimal") 273 | } 274 | for _, c := range e.Value { 275 | if c < '0' || c > '9' { 276 | return fmt.Errorf("invalid decimal %x", c) 277 | 278 | } 279 | } 280 | case TOperator: 281 | if i >= len(expr) { 282 | return fmt.Errorf("expect valid term for op %s", e.Value) 283 | } 284 | next := expr[i+1] 285 | if next.Type == TOperator || next.Type == 0 { 286 | return fmt.Errorf("expect valid term for op %s", e.Value) 287 | } 288 | } 289 | } 290 | return 291 | } 292 | -------------------------------------------------------------------------------- /lisa/expr_test.go: -------------------------------------------------------------------------------- 1 | package lisa 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | var good = [...]string{ 9 | `8000`, 10 | `$5-$3+$2`, 11 | `#LABEL`, 12 | `#$FF`, 13 | `#!6253`, 14 | `#%1011001`, 15 | `#'A'`, 16 | `#"Z"+$1`, 17 | `LBL+$3`, 18 | `HERE-THERE`, 19 | `*+!10`, 20 | `"Z"+$1`, 21 | `$FF`, 22 | `!10`, 23 | `!-936`, 24 | `LABEL/2*X^$FFFF&$10FF|1`, 25 | `LBL-$FF+!10-%1010011`, 26 | `/LABEL`, 27 | `/$FF`, 28 | `/!6253`, 29 | `/%101001100`, 30 | `/LBL+$4`, 31 | `/$F88F`, 32 | } 33 | 34 | var bad = map[string]string{ 35 | `/LBL+`: "expect valid term", 36 | `/LBL+$`: "empty hex", 37 | `/LBL+$G`: "invalid hex", 38 | } 39 | 40 | func TestTokenizeExpr(t *testing.T) { 41 | for i, g := range good { 42 | c := []string{} 43 | _, e, order, err := parseOperand([]byte(g)) 44 | c = append(c, "ORDER:"+string(order)) 45 | for term := e; term != nil; term = term.next { 46 | c = append(c, term.String()) 47 | } 48 | t.Log(i, g, strings.Join(c, " <-> ")) 49 | if err != nil { 50 | t.Errorf("%s:%v", g, err) 51 | } 52 | } 53 | } 54 | 55 | func TestSyntaxExpr(t *testing.T) { 56 | for k, v := range bad { 57 | _, e, _, err := parseOperand([]byte(k)) 58 | if err != nil || e == nil { 59 | t.Errorf("%s:%v", k, err) 60 | } 61 | err = syntaxCheck(e) 62 | if err == nil { 63 | t.Errorf("expecting %s err=%s got nil", k, v) 64 | continue 65 | } 66 | if !strings.Contains(err.Error(), v) { 67 | t.Errorf("expecting %s err=%s, got=%v", k, v, err) 68 | } 69 | } 70 | } 71 | 72 | func TestTermUint16(t *testing.T) { 73 | 74 | for k, v := range map[string]uint16{ 75 | "$16": 0x16, 76 | "!16": 16, 77 | "%0101": 5, 78 | "$42": 0x42, 79 | "$FF": 0xff, 80 | "$FFFF": 0xFFff, 81 | } { 82 | _, e, _, err := parseOperand([]byte(k)) 83 | if err != nil { 84 | t.Error(err) 85 | } 86 | u, err := e.Uint16() 87 | if err != nil { 88 | t.Error(err) 89 | } 90 | if u != v { 91 | t.Errorf("%s => 0x%x, expect 0x%x", k, u, v) 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /lisa/link.go: -------------------------------------------------------------------------------- 1 | package lisa 2 | 3 | import ( 4 | "encoding/json" 5 | "go6502/ins" 6 | "log" 7 | "os" 8 | ) 9 | 10 | const ( 11 | textSeg = "_TEXT" 12 | dataSeg = "_DATA" 13 | bssSeg = "_BSS" 14 | ) 15 | 16 | func NewLink(fl []string, cfg string) (l *Link, err error) { 17 | l = &Link{ 18 | SymList: make(map[string]*Symbol), 19 | } 20 | err = json.Unmarshal([]byte(cfg), l) 21 | l.fileList = fl 22 | return 23 | } 24 | 25 | type Link struct { 26 | ErrCount int `json:"-"` 27 | out string `json:"-"` 28 | fileList []string `json:"-"` 29 | SymList map[string]*Symbol `json:"-"` 30 | Block []*Block `json:"-"` 31 | 32 | Text uint16 `json:"text"` 33 | BSS uint16 `json:"bss"` 34 | Data uint16 `json:"data"` 35 | } 36 | 37 | func (l *Link) Errorf(f string, args ...interface{}) { 38 | if l.ErrCount > 10 { 39 | log.Fatal("too many error") 40 | } 41 | l.ErrCount++ 42 | log.Printf(f, args) 43 | } 44 | 45 | func (l *Link) Load() { 46 | for _, f := range l.fileList { 47 | fd, err := os.Open(f) 48 | if err != nil { 49 | l.Errorf("load:%s", err) 50 | continue 51 | } 52 | dec := json.NewDecoder(fd) 53 | of := &ObjFile{} 54 | err = dec.Decode(of) 55 | fd.Close() 56 | if err != nil { 57 | l.Errorf("parse:%s", err) 58 | continue 59 | } 60 | for _, blk := range of.Block { 61 | l.Block = append(l.Block, blk) 62 | for _, sym := range blk.List { 63 | 64 | lb := sym.Stmt.Label 65 | if lb == "" || isLocalLabel(lb) { 66 | continue 67 | } 68 | ns, ok := l.SymList[lb] 69 | if ok { 70 | if ns.Stmt.Expr.String() == sym.Stmt.Expr.String() { 71 | continue 72 | } 73 | l.Errorf("conflict label:%s %s ||%s", lb, sym.Stmt, ns.Stmt) 74 | } 75 | l.SymList[lb] = sym 76 | } 77 | } 78 | } 79 | return 80 | } 81 | 82 | func (l *Link) evalueTerm(blk *Block, sym *Symbol, t *Term) (o int32, err error) { 83 | switch t.Type { 84 | case TBinary, TDecimal, THex: 85 | var ui uint16 86 | ui, err = t.Uint16() 87 | o = int32(ui) 88 | case TLabel: 89 | n := string(t.Value) 90 | s, ok := l.SymList[n] 91 | if !ok { 92 | l.Errorf("can't find symbol:%s", n) 93 | return 94 | } 95 | if s.Type == Constants { 96 | o, err = l.evalueExpr(blk, s) 97 | } else { 98 | o = int32(s.Addr) 99 | } 100 | case TCurrentLine: 101 | o = int32(sym.Addr) 102 | case TLSLabel: 103 | sum := int8(0) 104 | var i int 105 | for i := range blk.List { 106 | if blk.List[i] == sym { 107 | break 108 | } 109 | } 110 | for ; i >= 0; i-- { 111 | label := blk.List[i].Stmt.Label 112 | sum -= int8(blk.List[i].Size) 113 | if isLocalLabel(label) && label[1:] == string(t.Value) { 114 | o = int32(sum) 115 | return 116 | } 117 | } 118 | l.Errorf("can't find LSLabel for:%s", string(t.Value)) 119 | case TGTLabel: 120 | sum := int8(0) 121 | var i int 122 | for i := range blk.List { 123 | if blk.List[i] == sym { 124 | break 125 | } 126 | } 127 | for ; i < len(blk.List); i++ { 128 | label := blk.List[i].Stmt.Label 129 | sum += int8(blk.List[i].Size) 130 | if isLocalLabel(label) && label[1:] == string(t.Value) { 131 | o = int32(sum) 132 | return 133 | } 134 | } 135 | l.Errorf("can't find GTLabel for:%s", string(t.Value)) 136 | case TRaw: 137 | o = int32(sym.Addr) 138 | default: 139 | l.Errorf("unsupported type:%s", t.Type) 140 | } 141 | return 142 | } 143 | 144 | func (l *Link) evalueExpr(blk *Block, sym *Symbol) (o int32, err error) { 145 | 146 | ex := sym.Stmt.Expr 147 | if len(ex) == 0 { 148 | return 149 | } 150 | 151 | el := len(ex) - 1 152 | o, err = l.evalueTerm(blk, sym, ex[el]) 153 | if err != nil { 154 | return 155 | } 156 | ex = ex[:el] 157 | 158 | // NOTE from right to left!!! 159 | for i := len(ex) - 1; i >= 0; i -= 2 { 160 | xt, operator := ex[i-1], ex[i] 161 | if operator.Type != TOperator { 162 | err = sym.Stmt.NE("require operator got:%s", operator) 163 | return 164 | } 165 | 166 | var x int32 167 | x, err = l.evalueTerm(blk, sym, xt) 168 | if err != nil { 169 | return 170 | } 171 | 172 | switch operator.Value[0] { 173 | case '+': 174 | o = x + o 175 | case '-': 176 | o = x - o 177 | case '*': 178 | o = x * o 179 | case '/': 180 | o = x / o 181 | case '&': 182 | o = int32(uint16(x) & uint16(o)) 183 | case '|': 184 | o = int32(uint16(x) | uint16(o)) 185 | case '^': 186 | o = int32(uint16(x) ^ uint16(o)) 187 | default: 188 | err = sym.Stmt.NE("unsuppored operator:%s", operator) 189 | return 190 | } 191 | } 192 | return 193 | } 194 | 195 | func (l *Link) Resolve() { 196 | for _, blk := range l.Block { 197 | es := "" 198 | if blk.Origin == nil { 199 | es = textSeg // default text 200 | l.set(es, blk) 201 | continue 202 | } 203 | 204 | expr := blk.Origin.Stmt.Expr 205 | if len(expr) == 1 { 206 | es := string(expr[0].Value) 207 | switch es { 208 | case textSeg, dataSeg, bssSeg: 209 | l.set(es, blk) 210 | continue 211 | } 212 | } 213 | o, err := l.evalueExpr(blk, blk.Origin) 214 | if err != nil || o < 0 { 215 | l.Errorf("origin resolve Failed:%s", err) 216 | } 217 | blk.Origin.Addr = uint16(o) 218 | l.set("", blk) 219 | } 220 | 221 | for _, blk := range l.Block { 222 | for _, sym := range blk.List { 223 | if IsNonAddress(sym.Stmt.Mnemonic) { 224 | continue 225 | } 226 | var err error 227 | sym.Operand, err = l.evalueExpr(blk, sym) 228 | if err != nil { 229 | l.Errorf("evalue :%s", err) 230 | } 231 | if sym.Stmt.Mode == ins.Relative { 232 | sym.Operand = sym.Operand - int32(sym.Addr) - 2 233 | } 234 | } 235 | } 236 | } 237 | 238 | func (l *Link) set(seg string, blk *Block) { 239 | if blk.Origin == nil { 240 | blk.Origin = &Symbol{} 241 | } 242 | switch seg { 243 | case textSeg: 244 | blk.Origin.Addr = l.Text 245 | l.Text += blk.Size 246 | case dataSeg: 247 | blk.Origin.Addr = l.Data 248 | l.Data += blk.Size 249 | case bssSeg: 250 | blk.Origin.Addr = l.BSS 251 | l.BSS += blk.Size 252 | default: 253 | } 254 | org := uint16(blk.Origin.Addr) 255 | 256 | // TODO check overlay 257 | 258 | for _, sym := range blk.List { 259 | if IsNonAddress(sym.Stmt.Mnemonic) { 260 | continue 261 | } 262 | sym.Addr = org 263 | org += uint16(sym.Size) 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /lisa/mnemonic_defs.go: -------------------------------------------------------------------------------- 1 | package lisa 2 | 3 | type Mnemonic uint16 4 | 5 | //go:generate stringer --type=Mnemonic 6 | const ( 7 | ADC Mnemonic = iota + 1 8 | AND 9 | ASL 10 | BCC 11 | BCS 12 | BEQ 13 | BIT 14 | BNE 15 | BPL 16 | BRK 17 | BVC 18 | BVS 19 | CLC 20 | CLD 21 | CLI 22 | CLV 23 | CMP 24 | CPX 25 | CPY 26 | DEC 27 | DEX 28 | DEY 29 | EOR 30 | INC 31 | INX 32 | INY 33 | JMP 34 | JSR 35 | LDA 36 | LDX 37 | LDY 38 | LSR 39 | NOP 40 | ORA 41 | PHA 42 | PHP 43 | PLA 44 | PLP 45 | ROL 46 | ROR 47 | RTI 48 | RTS 49 | SBC 50 | SEC 51 | SED 52 | SEI 53 | STA 54 | STX 55 | STY 56 | TAX 57 | TAY 58 | TSX 59 | TXA 60 | TXS 61 | TYA 62 | BTR 63 | BFL 64 | BGE 65 | BLT 66 | XOR 67 | SET 68 | LDR 69 | STO 70 | LDD 71 | STD 72 | POP 73 | STP 74 | ADD 75 | SUB 76 | PPD 77 | CPR 78 | INR 79 | DCR 80 | RTN 81 | BRA 82 | BNC 83 | BIC 84 | BIP 85 | BIM 86 | BMI 87 | BNM 88 | BKS 89 | RSB 90 | BSB 91 | BNZ 92 | OBJ 93 | ORG 94 | EPZ 95 | EQU 96 | ASC 97 | STR 98 | HEX 99 | LST 100 | NLS 101 | DCM 102 | ICL // Inclide file 103 | END 104 | ADR 105 | DCI 106 | INV 107 | BLK 108 | DFS 109 | PAG 110 | PAU 111 | BYT 112 | HBY 113 | DBY 114 | LET 115 | TTL 116 | NOG 117 | GEN 118 | PHS 119 | DPH 120 | DA // dots prefix 121 | IF 122 | EL 123 | FI 124 | USR 125 | ) 126 | 127 | func IsNonAddress(m Mnemonic) bool { 128 | switch m { 129 | case OBJ, ORG, EPZ, EQU, ICL, END: 130 | return true 131 | } 132 | return false 133 | } 134 | 135 | func isRawData(m Mnemonic) bool { 136 | switch m { 137 | case ASC, STR, HEX, ADR: 138 | return true 139 | } 140 | return false 141 | } 142 | 143 | func isEnd(m Mnemonic) bool { 144 | switch m { 145 | case RTI, RTS, END: 146 | return true 147 | } 148 | return false 149 | } 150 | 151 | func isRelative(m Mnemonic) bool { 152 | switch m { 153 | case BCC, BCS, BEQ, BNE, 154 | BNC, BPL, BTR, BFL, 155 | BGE, BLT, BMI, BVC, BVS: 156 | return true 157 | } 158 | return false 159 | } 160 | -------------------------------------------------------------------------------- /lisa/mnemonic_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer --type=Mnemonic"; DO NOT EDIT. 2 | 3 | package lisa 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[ADC-1] 12 | _ = x[AND-2] 13 | _ = x[ASL-3] 14 | _ = x[BCC-4] 15 | _ = x[BCS-5] 16 | _ = x[BEQ-6] 17 | _ = x[BIT-7] 18 | _ = x[BNE-8] 19 | _ = x[BPL-9] 20 | _ = x[BRK-10] 21 | _ = x[BVC-11] 22 | _ = x[BVS-12] 23 | _ = x[CLC-13] 24 | _ = x[CLD-14] 25 | _ = x[CLI-15] 26 | _ = x[CLV-16] 27 | _ = x[CMP-17] 28 | _ = x[CPX-18] 29 | _ = x[CPY-19] 30 | _ = x[DEC-20] 31 | _ = x[DEX-21] 32 | _ = x[DEY-22] 33 | _ = x[EOR-23] 34 | _ = x[INC-24] 35 | _ = x[INX-25] 36 | _ = x[INY-26] 37 | _ = x[JMP-27] 38 | _ = x[JSR-28] 39 | _ = x[LDA-29] 40 | _ = x[LDX-30] 41 | _ = x[LDY-31] 42 | _ = x[LSR-32] 43 | _ = x[NOP-33] 44 | _ = x[ORA-34] 45 | _ = x[PHA-35] 46 | _ = x[PHP-36] 47 | _ = x[PLA-37] 48 | _ = x[PLP-38] 49 | _ = x[ROL-39] 50 | _ = x[ROR-40] 51 | _ = x[RTI-41] 52 | _ = x[RTS-42] 53 | _ = x[SBC-43] 54 | _ = x[SEC-44] 55 | _ = x[SED-45] 56 | _ = x[SEI-46] 57 | _ = x[STA-47] 58 | _ = x[STX-48] 59 | _ = x[STY-49] 60 | _ = x[TAX-50] 61 | _ = x[TAY-51] 62 | _ = x[TSX-52] 63 | _ = x[TXA-53] 64 | _ = x[TXS-54] 65 | _ = x[TYA-55] 66 | _ = x[BTR-56] 67 | _ = x[BFL-57] 68 | _ = x[BGE-58] 69 | _ = x[BLT-59] 70 | _ = x[XOR-60] 71 | _ = x[SET-61] 72 | _ = x[LDR-62] 73 | _ = x[STO-63] 74 | _ = x[LDD-64] 75 | _ = x[STD-65] 76 | _ = x[POP-66] 77 | _ = x[STP-67] 78 | _ = x[ADD-68] 79 | _ = x[SUB-69] 80 | _ = x[PPD-70] 81 | _ = x[CPR-71] 82 | _ = x[INR-72] 83 | _ = x[DCR-73] 84 | _ = x[RTN-74] 85 | _ = x[BRA-75] 86 | _ = x[BNC-76] 87 | _ = x[BIC-77] 88 | _ = x[BIP-78] 89 | _ = x[BIM-79] 90 | _ = x[BMI-80] 91 | _ = x[BNM-81] 92 | _ = x[BKS-82] 93 | _ = x[RSB-83] 94 | _ = x[BSB-84] 95 | _ = x[BNZ-85] 96 | _ = x[OBJ-86] 97 | _ = x[ORG-87] 98 | _ = x[EPZ-88] 99 | _ = x[EQU-89] 100 | _ = x[ASC-90] 101 | _ = x[STR-91] 102 | _ = x[HEX-92] 103 | _ = x[LST-93] 104 | _ = x[NLS-94] 105 | _ = x[DCM-95] 106 | _ = x[ICL-96] 107 | _ = x[END-97] 108 | _ = x[ADR-98] 109 | _ = x[DCI-99] 110 | _ = x[INV-100] 111 | _ = x[BLK-101] 112 | _ = x[DFS-102] 113 | _ = x[PAG-103] 114 | _ = x[PAU-104] 115 | _ = x[BYT-105] 116 | _ = x[HBY-106] 117 | _ = x[DBY-107] 118 | _ = x[LET-108] 119 | _ = x[TTL-109] 120 | _ = x[NOG-110] 121 | _ = x[GEN-111] 122 | _ = x[PHS-112] 123 | _ = x[DPH-113] 124 | _ = x[DA-114] 125 | _ = x[IF-115] 126 | _ = x[EL-116] 127 | _ = x[FI-117] 128 | _ = x[USR-118] 129 | } 130 | 131 | const _Mnemonic_name = "ADCANDASLBCCBCSBEQBITBNEBPLBRKBVCBVSCLCCLDCLICLVCMPCPXCPYDECDEXDEYEORINCINXINYJMPJSRLDALDXLDYLSRNOPORAPHAPHPPLAPLPROLRORRTIRTSSBCSECSEDSEISTASTXSTYTAXTAYTSXTXATXSTYABTRBFLBGEBLTXORSETLDRSTOLDDSTDPOPSTPADDSUBPPDCPRINRDCRRTNBRABNCBICBIPBIMBMIBNMBKSRSBBSBBNZOBJORGEPZEQUASCSTRHEXLSTNLSDCMICLENDADRDCIINVBLKDFSPAGPAUBYTHBYDBYLETTTLNOGGENPHSDPHDAIFELFIUSR" 132 | 133 | var _Mnemonic_index = [...]uint16{0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126, 129, 132, 135, 138, 141, 144, 147, 150, 153, 156, 159, 162, 165, 168, 171, 174, 177, 180, 183, 186, 189, 192, 195, 198, 201, 204, 207, 210, 213, 216, 219, 222, 225, 228, 231, 234, 237, 240, 243, 246, 249, 252, 255, 258, 261, 264, 267, 270, 273, 276, 279, 282, 285, 288, 291, 294, 297, 300, 303, 306, 309, 312, 315, 318, 321, 324, 327, 330, 333, 336, 339, 341, 343, 345, 347, 350} 134 | 135 | func (i Mnemonic) String() string { 136 | i -= 1 137 | if i >= Mnemonic(len(_Mnemonic_index)-1) { 138 | return "Mnemonic(" + strconv.FormatInt(int64(i+1), 10) + ")" 139 | } 140 | return _Mnemonic_name[_Mnemonic_index[i]:_Mnemonic_index[i+1]] 141 | } 142 | -------------------------------------------------------------------------------- /lisa/objfile.go: -------------------------------------------------------------------------------- 1 | package lisa 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | ) 7 | 8 | type Block struct { 9 | Origin *Symbol `json:"origin"` 10 | Table map[string]int `json:"table"` // name -> list index 11 | List []*Symbol `json:"list"` 12 | Size uint16 `json:"size"` 13 | } 14 | 15 | type ObjFile struct { 16 | Block []*Block `json:"block"` 17 | } 18 | 19 | func NewObjFile(sl []*Symbol) (of *ObjFile) { 20 | of = &ObjFile{ 21 | Block: []*Block{&Block{Table: make(map[string]int)}}, 22 | } 23 | blk := of.Block[0] 24 | 25 | if len(sl) > 1 && sl[0].Stmt.Mnemonic == ORG { 26 | blk.Origin = sl[0] 27 | sl = sl[1:] 28 | } 29 | 30 | for _, sym := range sl { 31 | if sym.Stmt.Mnemonic == ORG { 32 | blk = &Block{ 33 | Origin: sym, 34 | List: []*Symbol{sym}, 35 | Table: make(map[string]int), 36 | } 37 | of.Block = append(of.Block, blk) 38 | 39 | if sym.Stmt.Label != "" { 40 | blk.Table[sym.Stmt.Label] = 0 41 | } 42 | continue 43 | } 44 | blk.Size += uint16(sym.Size) 45 | blk.List = append(blk.List, sym) 46 | 47 | if sym.Stmt.Label != "" { 48 | blk.Table[sym.Stmt.Label] = len(blk.List) - 1 49 | } 50 | 51 | } 52 | return 53 | } 54 | 55 | func (of *ObjFile) WriteTo(wt io.Writer) (err error) { 56 | enc := json.NewEncoder(wt) 57 | enc.SetIndent("", " ") 58 | err = enc.Encode(of) 59 | return 60 | } 61 | -------------------------------------------------------------------------------- /lisa/operator.go: -------------------------------------------------------------------------------- 1 | package lisa 2 | 3 | const ( 4 | OpAdd byte = '+' 5 | OpAnd = '&' 6 | OpBinary = '%' 7 | OpAsterisk = '*' 8 | OpDecimal = '!' 9 | OpEqual = '=' 10 | OpHex = '$' 11 | OpDivision = '/' 12 | OpLowOrder = '#' 13 | OpMinus = '-' 14 | OpOr = '|' 15 | OpXor = '^' 16 | OpApostrophe = '\'' 17 | OpQuote = '"' 18 | OpLeftBracket = '(' 19 | OpRightBracket = ')' 20 | OpLSLabel = '<' 21 | OpGTLabel = '>' 22 | ) 23 | -------------------------------------------------------------------------------- /lisa/parser.go: -------------------------------------------------------------------------------- 1 | // Asm parser for 6502/Apple ][ 2 | package lisa 3 | 4 | import ( 5 | "bufio" 6 | "bytes" 7 | "fmt" 8 | "go6502/ins" 9 | "io" 10 | "os" 11 | "strings" 12 | ) 13 | 14 | var letters []byte 15 | var lan []byte 16 | var hex []byte 17 | var mnemonicMap map[string]Mnemonic 18 | 19 | func init() { 20 | for i := byte('0'); i <= '9'; i++ { 21 | lan = append(lan, i) 22 | hex = append(hex, i) 23 | } 24 | for i := byte('a'); i <= 'z'; i++ { 25 | if i <= 'f' { 26 | hex = append(hex, i) 27 | } 28 | letters = append(letters, i) 29 | lan = append(lan, i) 30 | } 31 | for i := byte('A'); i <= 'Z'; i++ { 32 | if i <= 'F' { 33 | hex = append(hex, i) 34 | } 35 | lan = append(lan, i) 36 | letters = append(letters, i) 37 | } 38 | mnemonicMap = map[string]Mnemonic{} 39 | for i := ADC; i <= USR; i++ { 40 | mnemonicMap[i.String()] = i 41 | } 42 | mnemonicMap["="] = EQU 43 | } 44 | 45 | type Stmt struct { 46 | Line int 47 | Label string 48 | Mnemonic Mnemonic 49 | Oper string 50 | Comment string 51 | Order byte 52 | Expr Expression 53 | Mode ins.Mode 54 | } 55 | 56 | func (s *Stmt) NE(f string, args ...interface{}) error { 57 | args = append(args, s.Line) 58 | if len(args) == 1 { 59 | return fmt.Errorf("Line:%d "+f, s.Line) 60 | } 61 | l := len(args) - 1 62 | args[0], args[l] = args[l], args[0] 63 | return fmt.Errorf("Line:%d "+f, args...) 64 | } 65 | 66 | func checkLabels(il []*Stmt) (err error) { 67 | // build label table 68 | lt := map[string]*Stmt{} 69 | el := []error{} 70 | for _, s := range il { 71 | if len(el) > 10 { 72 | break 73 | } 74 | if s.Label != "" && !isLocalLabel(s.Label) { 75 | if ps, ok := lt[s.Label]; ok { 76 | el = append(el, fmt.Errorf("Line %d duplicate regular label %q, previous defined at line %d", 77 | s.Line, s.Label, ps.Line)) 78 | continue 79 | } 80 | lt[s.Label] = s 81 | } 82 | } 83 | 84 | err = elToError(el) 85 | if err != nil { 86 | return 87 | } 88 | end: 89 | // Find local Label 90 | for i, s := range il { 91 | if len(s.Expr) == 0 { 92 | continue 93 | } 94 | next: 95 | for _, t := range s.Expr { 96 | if len(el) > 10 { 97 | el = append(el, fmt.Errorf("too many errors")) 98 | break end 99 | } 100 | switch t.Type { 101 | case TLSLabel: 102 | tl := il[:i] 103 | for j := len(tl) - 1; j >= 0; j-- { 104 | jt := tl[j] 105 | if strings.HasPrefix(jt.Label, "^") && jt.Label[1:] == string(t.Value) { 106 | break next 107 | } 108 | } 109 | el = append(el, s.NE("can't find local label:%s", string(t.Value))) 110 | 111 | case TGTLabel: 112 | for _, jt := range il[i:] { 113 | if strings.HasPrefix(jt.Label, "^") && jt.Label[1:] == string(t.Value) { 114 | break next 115 | } 116 | } 117 | el = append(el, s.NE("can't find local label:%s", string(t.Value))) 118 | } 119 | } 120 | } 121 | 122 | err = elToError(el) 123 | return 124 | } 125 | 126 | func semantic(il []*Stmt) (err error) { 127 | type checker func(il []*Stmt) error 128 | for _, f := range []checker{ 129 | checkLabels, 130 | } { 131 | err = f(il) 132 | if err != nil { 133 | return 134 | } 135 | } 136 | return 137 | } 138 | 139 | func (l *Stmt) String() string { 140 | return fmt.Sprintf("[%4d] L:%-8s %s O[%s]%s M:%s %s", l.Line, l.Label, l.Mnemonic, 141 | string(l.Order), l.Expr, l.Mode, l.Comment) 142 | } 143 | 144 | func elToError(el []error) (err error) { 145 | if len(el) == 0 { 146 | return nil 147 | } 148 | buf := []string{} 149 | for _, e := range el { 150 | buf = append(buf, e.Error()) 151 | } 152 | return fmt.Errorf(strings.Join(buf, "\n")) 153 | } 154 | 155 | func parse(il []*Stmt) (err error) { 156 | 157 | el := []error{} 158 | 159 | for _, s := range il { 160 | if s.Oper == "" { 161 | continue 162 | } 163 | s.Mode, s.Expr, s.Order, err = parseOperand([]byte(s.Oper)) 164 | if err != nil { 165 | el = append(el, fmt.Errorf("Line:%d %s", s.Line, err)) 166 | if len(el) > 10 { 167 | el = append(el, fmt.Errorf("too many error")) 168 | break 169 | } 170 | } 171 | if len(s.Expr) == 0 { 172 | continue 173 | } 174 | err = syntaxCheck(s.Expr) 175 | if err != nil { 176 | el = append(el, fmt.Errorf("Line:%d %s", s.Line, err)) 177 | if len(el) > 10 { 178 | break 179 | } 180 | } 181 | } 182 | return elToError(el) 183 | } 184 | 185 | func Parse(rd io.Reader) (il []*Stmt, err error) { 186 | sc := bufio.NewScanner(rd) 187 | for ln := 1; sc.Scan(); ln++ { 188 | t := sc.Bytes() 189 | if len(t) == 0 || len(bytes.TrimSpace(t)) == 0 { 190 | // skip empty line 191 | continue 192 | } 193 | 194 | st := &Stmt{} 195 | if err = lexing(st, t); err != nil { 196 | return 197 | } 198 | 199 | st.Line = ln 200 | if st.Mnemonic != ICL { 201 | il = append(il, st) 202 | continue 203 | } 204 | 205 | var fd *os.File 206 | fd, err = os.Open(st.Oper[1 : len(st.Oper)-1]) 207 | if err != nil { 208 | return 209 | } 210 | var stl []*Stmt 211 | stl, err = Parse(fd) 212 | if err != nil { 213 | return 214 | } 215 | fd.Close() 216 | il = append(il, stl...) 217 | } 218 | err = parse(il) 219 | if err != nil { 220 | return 221 | } 222 | err = semantic(il) 223 | return 224 | } 225 | 226 | func lexing(l *Stmt, t []byte) (err error) { 227 | 228 | for len(t) > 0 && err == nil { 229 | c := t[0] 230 | switch c { 231 | case ' ', '\t': 232 | if l.Label == "" { 233 | l.Label = "\t" 234 | } 235 | t = t[1:] 236 | case ';': 237 | l.Comment = string(t) 238 | t = t[:0] 239 | case '*': 240 | if l.Mnemonic == 0 { 241 | l.Comment = string(t) 242 | t = t[:0] 243 | continue 244 | } 245 | fallthrough 246 | default: 247 | i := bytes.IndexAny(t, "\t \n\r") 248 | if i == -1 { 249 | i = len(t) 250 | } 251 | 252 | var w []byte 253 | // XXX How to do better ? 254 | switch l.Mnemonic { 255 | case ASC, STR, ICL: 256 | w, t, err = lookForQuote(t) 257 | if err != nil { 258 | return l.NE(err.Error()) 259 | } 260 | default: 261 | w, t = t[:i], t[i:] 262 | } 263 | 264 | if l.Label == "" { 265 | err = l.processLabel(w) 266 | continue 267 | } 268 | 269 | if l.Mnemonic == 0 { 270 | err = l.handleMnemonic(w) 271 | l.Mode = ins.Implied 272 | continue 273 | } 274 | 275 | if l.Oper == "" { 276 | i := bytes.IndexAny(t, "\t;\n\r") 277 | if i == -1 { 278 | i = len(t) 279 | } 280 | w = append(w, t[:i]...) 281 | t = t[i:] 282 | l.Oper = string(w) 283 | } 284 | } 285 | } 286 | 287 | if l.Label == "\t" { 288 | l.Label = "" 289 | } 290 | 291 | return 292 | } 293 | 294 | func (l *Stmt) processLabel(b []byte) (err error) { 295 | // local label 296 | l.Label = strings.ToUpper(string(b)) 297 | return 298 | } 299 | 300 | func (l *Stmt) handleMnemonic(b []byte) (err error) { 301 | if len(b) > 3 { 302 | return fmt.Errorf("invalid mnemonic %s", string(b)) 303 | } 304 | b = bytes.ToUpper(b) 305 | // trim 306 | if b[0] == '.' { 307 | b = b[1:] 308 | } 309 | m, ok := mnemonicMap[string(b)] 310 | if !ok { 311 | return fmt.Errorf("invalid mnemonic %s", string(b)) 312 | } 313 | l.Mnemonic = m 314 | return 315 | } 316 | 317 | func lookForQuote(t []byte) (w, r []byte, err error) { 318 | if len(t) < 2 { 319 | err = fmt.Errorf("quotation must more than 2, got=%q", string(t)) 320 | return 321 | } 322 | start := t[0] 323 | switch start { 324 | case '\'', '"': 325 | default: 326 | err = fmt.Errorf("looking for quote, got: %s ", string(t)) 327 | return 328 | } 329 | l := bytes.LastIndexByte(t[1:], start) 330 | if l == -1 { 331 | err = fmt.Errorf("can't find matched start: %s", string(start)) 332 | return 333 | } 334 | w, r = t[:l+2], t[l+2:] 335 | return 336 | } 337 | -------------------------------------------------------------------------------- /lisa/stype_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer --type=SType"; DO NOT EDIT. 2 | 3 | package lisa 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[Default-1] 12 | _ = x[Constants-2] 13 | _ = x[RawData-4] 14 | _ = x[Jump-8] 15 | _ = x[Branch-16] 16 | } 17 | 18 | const ( 19 | _SType_name_0 = "DefaultConstants" 20 | _SType_name_1 = "RawData" 21 | _SType_name_2 = "Jump" 22 | _SType_name_3 = "Branch" 23 | ) 24 | 25 | var ( 26 | _SType_index_0 = [...]uint8{0, 7, 16} 27 | ) 28 | 29 | func (i SType) String() string { 30 | switch { 31 | case 1 <= i && i <= 2: 32 | i -= 1 33 | return _SType_name_0[_SType_index_0[i]:_SType_index_0[i+1]] 34 | case i == 4: 35 | return _SType_name_1 36 | case i == 8: 37 | return _SType_name_2 38 | case i == 16: 39 | return _SType_name_3 40 | default: 41 | return "SType(" + strconv.FormatInt(int64(i), 10) + ")" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lisa/termtype_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer --type=TermType --trimprefix=T"; DO NOT EDIT. 2 | 3 | package lisa 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[TLabel-1] 12 | _ = x[TOperator-2] 13 | _ = x[THex-3] 14 | _ = x[TDecimal-4] 15 | _ = x[TBinary-5] 16 | _ = x[TAscii-6] 17 | _ = x[TCurrentLine-7] 18 | _ = x[TLSLabel-8] 19 | _ = x[TGTLabel-9] 20 | _ = x[TRaw-10] 21 | } 22 | 23 | const _TermType_name = "LabelOperatorHexDecimalBinaryAsciiCurrentLineLSLabelGTLabelRaw" 24 | 25 | var _TermType_index = [...]uint8{0, 5, 13, 16, 23, 29, 34, 45, 52, 59, 62} 26 | 27 | func (i TermType) String() string { 28 | i -= 1 29 | if i >= TermType(len(_TermType_index)-1) { 30 | return "TermType(" + strconv.FormatInt(int64(i+1), 10) + ")" 31 | } 32 | return _TermType_name[_TermType_index[i]:_TermType_index[i+1]] 33 | } 34 | -------------------------------------------------------------------------------- /lisa/testdata/basic.s: -------------------------------------------------------------------------------- 1 | ******** 2 | * Test * 3 | ******** 4 | 5 | ; This is a test comment 6 | ORG LABEL 7 | SYMBOLIC EQU $3 8 | LABEL = $3 9 | LBL = LABEL-SYMBOLIC 10 | OK = LABEL-SYMBOLIC 11 | LDA LABEL+$1 12 | LDA $1 13 | LBL1 LDA $800 14 | LDA LBL,X 15 | LDA LBL+$1,X 16 | LDA $10,X 17 | LDA $1010,X 18 | LDA LBL,Y 19 | START EQU $8000 20 | STA $80,Y 21 | LDX $0,Y 22 | GT BNE >6 23 | BCS GT+$3 24 | ^6 BVC *+$5 25 | BMI <6 26 | BEQ LBL-$3 27 | BEQ >7-$3 28 | LDA (LBL),Y 29 | LDA (LBL+$2),Y 30 | LDA ($2),Y 31 | LDA (!10+%101),Y 32 | LDA (LBL,X) 33 | ADC (LBL+$3,X) 34 | STA (LABEL-!2,X) 35 | STA ($00,X) 36 | JMP (LBL) 37 | JMP (LBL+$3) 38 | JMP ($800) 39 | ^0 LDA #666 40 | ^9 STA <0 41 | ^7 BIT $C010 42 | BRK 43 | -------------------------------------------------------------------------------- /lisa/testdata/hello_world.s: -------------------------------------------------------------------------------- 1 | ICL "lisa/testdata/basic.s" 2 | * LISA From 2021 3 | ORG $400 // start at 0x400 4 | STA ($40),Y 5 | -------------------------------------------------------------------------------- /lisa/testdata/hex.s: -------------------------------------------------------------------------------- 1 | L1 = $1000 2 | L2 = $C000 3 | LOGO = L1+L2 4 | ORG LOGO 5 | HEX DEAD_BEEF 6 | ASC 'ASC World' + 'AFFF' 7 | STR 'STRING HELLO' + 'OK' 8 | -------------------------------------------------------------------------------- /lisa/testdata/labels.s: -------------------------------------------------------------------------------- 1 | ORG $8000 2 | L1 = L2+L3 3 | L2 = L4-L5 4 | L6 EPZ L2 5 | L4 = !1 6 | L5 = $1f 7 | L3 = L5+L7 8 | L7 EPZ $3 9 | CLC 10 | CLD 11 | CLI 12 | CLV 13 | DEX 14 | INX 15 | INY 16 | NOP 17 | PHA 18 | PHP 19 | PLA 20 | PLP 21 | RTI 22 | SEC 23 | SED 24 | SEI 25 | TAX 26 | TAY 27 | TSX 28 | TYA 29 | BRK 30 | RTS 31 | JMP (L7+!1) 32 | LDA L7+!1 33 | LDA /$123 34 | L8 LDA $23,X 35 | JMP (L8+1) 36 | -------------------------------------------------------------------------------- /lisa/testdata/simple_kbd.s: -------------------------------------------------------------------------------- 1 | * Simple KBD Reader Subroutine from AppleII 2 | * Meng Zhuo 2021 3 | * KEY Strobe at X 4 | 5 | START = $D000 6 | KBD = $C000 7 | KBDSTRB = $C010 8 | ADDR EPZ $00 9 | DST EPZ $42 10 | 11 | ORG $D000 12 | LDX #!0 13 | INNER LDA $e000,X 14 | STA $400,X 15 | INX 16 | CPX #!40 17 | BNE INNER 18 | 19 | LDX #!0 20 | INNER_1 LDA $e028,X 21 | STA $480,X 22 | INX 23 | CPX #!40 24 | BNE INNER_1 25 | 26 | LDX #!0 27 | INNER_2 LDA $e050,X 28 | STA $500,X 29 | INX 30 | CPX #!40 31 | BNE INNER_2 32 | 33 | LOOP NOP 34 | JMP LOOP ; main loop 35 | 36 | LDX KBDSTRB 37 | CPX #!0 38 | BEQ END 39 | LDX KBD 40 | STX DST 41 | END RTI 42 | -------------------------------------------------------------------------------- /zhuos/README.md: -------------------------------------------------------------------------------- 1 | ZHU OS 2 | ============= 3 | 4 | ZHU OS (Zhuo's Hardly Usable Operating System) is a simple OS 5 | for Go 6502 AppleII (emulator, 64 KByte) and fun only 6 | Some modification for easy implement of modern os. 7 | 8 | Modifications 9 | =============== 10 | 11 | 12 | 13 | 14 | Memory map 15 | ============== 16 | 17 | 0x0000 - 0x00ff Program 18 | 0x0100 - 0x01ff System stack 19 | 0x0400 - 0x07ff 40 cols text output page 1 (RW) (40x24) 20 | 0x0800 - 0x0bff 40 cols text output page 2 (RW) 21 | 22 | 0xc000 - 0xc010 Keyboard input 23 | 24 | 0xfffa - 0xfffb NMI handler (0x1000) 25 | 0xfffc - 0xfffd OS start point (0x2000) 26 | 0xfffe - 0xffff IRQ handler (0x3000) 27 | -------------------------------------------------------------------------------- /zhuos/src/keyin.s: -------------------------------------------------------------------------------- 1 | ICL 'zhuos/src/symbols.s' 2 | 3 | KBD = $C000 4 | KBDSTRB = $C010 5 | ASCBS = $08 | %10000000 6 | 7 | ORG _TEXT 8 | KEYIN LDA #$20 9 | LDY CH 10 | STA (BASL),Y ;REPLACE FLASHING SCREEN 11 | LDA KBD ;GET KEYCODE 12 | CMP #ASCBS ;BACK SPACE? (CNTRL-H) 13 | BEQ >1 14 | INC CH 15 | INC CH 16 | ^1 DEC CH 17 | AND #$7f 18 | STA (BASL),Y 19 | BIT KBDSTRB ;CLR KEY STROBE 20 | RTS 21 | 22 | -------------------------------------------------------------------------------- /zhuos/src/nmi.s: -------------------------------------------------------------------------------- 1 | ORG _TEXT 2 | NMIVECT NOP 3 | NOP 4 | NOP 5 | RTI 6 | -------------------------------------------------------------------------------- /zhuos/src/reset.s: -------------------------------------------------------------------------------- 1 | 2 | ORG _DATA 3 | OSNAME STR "ZHUOS" 4 | 5 | ORG _TEXT 6 | RESET CLD 7 | LDA #$04 ; init basic line low 8 | STA BASH 9 | LDA #!0 10 | CMP LOC0 11 | BEQ INIT 12 | JMP RESET 13 | INIT LDA #$5A 14 | STA (BASL),Y 15 | INY 16 | LDA #$48 17 | STA (BASL),Y 18 | INY 19 | LDA #$55 20 | STA (BASL),Y 21 | INY 22 | LDA #$4F 23 | STA (BASL),Y 24 | INY 25 | LDA #$53 26 | STA (BASL),Y 27 | INC LOC0 28 | LDA #$80 29 | STA BASL 30 | 31 | JMP RESET 32 | 33 | 34 | 35 | ORG _TEXT 36 | IRQENTRY NOP 37 | JSR KEYIN 38 | NOP 39 | RTI 40 | -------------------------------------------------------------------------------- /zhuos/src/symbols.s: -------------------------------------------------------------------------------- 1 | LOC0 = $00 2 | LOC1 = $01 3 | WNDLFT = $20 4 | WNDWDTH = $21 5 | WNDTOP = $22 6 | WNDBTM = $23 7 | CH = $24 8 | CV = $25 9 | GBASL = $26 10 | GBASH = $27 11 | BASL = $28 12 | BASH = $29 13 | BAS2L = $2A 14 | BAS2H = $2B 15 | H2 = $2C 16 | LMNEM = $2C 17 | RTNL = $2C 18 | V2 = $2D 19 | RMNEM = $2D 20 | RTNH = $2D 21 | MASK = $2E 22 | CHKSUM = $2E 23 | FORMAT = $2E 24 | LASTIN = $2F 25 | LENGTH = $2F 26 | SIGN = $2F 27 | COLOR = $30 28 | MODE = $31 29 | INVFLG = $32 30 | PROMPT = $33 31 | YSAV = $34 32 | YSAV1 = $35 33 | CSWL = $36 34 | CSWH = $37 35 | KSWL = $38 36 | KSWH = $39 37 | PCL = $3A 38 | PCH = $3B 39 | XQT = $3C 40 | A1L = $3C 41 | A1H = $3D 42 | A2L = $3E 43 | A2H = $3F 44 | A3L = $40 45 | A3H = $41 46 | A4L = $42 47 | A4H = $43 48 | A5L = $44 49 | A5H = $45 50 | ACC = $45 51 | XREG = $46 52 | YREG = $47 53 | STATUS = $48 54 | SPNT = $49 55 | RNDL = $4E 56 | RNDH = $4F 57 | ACL = $50 58 | ACH = $51 59 | XTNDL = $52 60 | XTNDH = $53 61 | AUXL = $54 62 | AUXH = $55 63 | PICK = $95 64 | 65 | IN = $0200 66 | 67 | USRADR = $03F8 68 | 69 | BASIC = $E000 70 | BASIC2 = $E003 71 | 72 | ASCLF = $8A 73 | ASCCR = $8D 74 | 75 | TEXTWIDTH = !40 76 | TEXTHEIGHT = !24 77 | 78 | * some const 79 | COUT = $FDED 80 | CROUT = $FD8E 81 | BELL = $FF3A 82 | -------------------------------------------------------------------------------- /zhuos/src/vectors.s: -------------------------------------------------------------------------------- 1 | ICL "zhuos/src/symbols.s" 2 | 3 | ORG $FFFA 4 | ADR NMIVECT ; NMI handler(0xfffa) 5 | ADR RESET ; Reset handler(0xfffc) 6 | ADR IRQENTRY ; IRQ handler(0xfffe) 7 | -------------------------------------------------------------------------------- /zhuos/zp/decode.go: -------------------------------------------------------------------------------- 1 | package zp 2 | 3 | import ( 4 | "encoding/gob" 5 | "io" 6 | ) 7 | 8 | func Decode(rd io.ReadSeeker, f *ZFile) (err error) { 9 | dec := gob.NewDecoder(rd) 10 | err = dec.Decode(f) 11 | return 12 | } 13 | -------------------------------------------------------------------------------- /zhuos/zp/encode.go: -------------------------------------------------------------------------------- 1 | package zp 2 | 3 | import ( 4 | "encoding/gob" 5 | "io" 6 | ) 7 | 8 | func Encode(wr io.WriteSeeker, f *ZFile) (err error) { 9 | enc := gob.NewEncoder(wr) 10 | err = enc.Encode(f) 11 | return 12 | } 13 | -------------------------------------------------------------------------------- /zhuos/zp/encode_test.go: -------------------------------------------------------------------------------- 1 | package zp 2 | 3 | import ( 4 | "io" 5 | "io/ioutil" 6 | "testing" 7 | ) 8 | 9 | func TestEncode(t *testing.T) { 10 | zf := &ZhuProg{} 11 | zf.Headers = []*ZHeader{ 12 | {ProgOffset: 0x4000}, 13 | {ProgOffset: 0x8000}, 14 | } 15 | 16 | zf.Progs = [][]byte{ 17 | []byte("Hello"), 18 | []byte("World"), 19 | } 20 | tf, err := ioutil.TempFile("", "") 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | defer tf.Close() 25 | err = Encode(tf, zf) 26 | if err != nil { 27 | t.Error(err) 28 | } 29 | _, err = tf.Seek(0, io.SeekStart) 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | t.Log(tf.Name()) 34 | } 35 | -------------------------------------------------------------------------------- /zhuos/zp/file.go: -------------------------------------------------------------------------------- 1 | // zp is file format of ZHUOS Program file format 2 | package zp 3 | 4 | type ZFile struct { 5 | L []*ZProg 6 | } 7 | 8 | type ZProg struct { 9 | Offset uint16 10 | Prog []byte 11 | } 12 | --------------------------------------------------------------------------------