Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: kballard/dcpu16
base: a0688be439
...
head fork: kballard/dcpu16
compare: 9c786451d1
Checking mergeability… Don't worry, you can still create the pull request.
  • 4 commits
  • 3 files changed
  • 0 commit comments
  • 1 contributor
Commits on Apr 12, 2012
@kballard Switch to github.com/kballard/termbox-go bf68b27
@kballard Implement support for colors 28ef149
Commits on Apr 13, 2012
@kballard Add flags to control the clock rate
Define a new flag -rate to set the clock rate for the machine.
Define a new flag -printRate to print the effective clock rate when the
machine is done.
78ec5b5
@kballard Add a ticker buffer
time.NewTicker drops ticks on the floor if the consumer takes too long.
We don't want that, so add a bit of a buffer. Leave it at 10 for the
moment. I don't know if this actually does anything useful, but it makes
me feel better.
9c78645
Showing with 125 additions and 24 deletions.
  1. +64 −16 dcpu/machine.go
  2. +39 −3 dcpu/video.go
  3. +22 −5 main.go
View
80 dcpu/machine.go
@@ -4,6 +4,8 @@ import (
"errors"
"fmt"
"github.com/kballard/dcpu16/dcpu/core"
+ "io"
+ "strings"
"time"
)
@@ -25,11 +27,11 @@ func (err *MachineError) Error() string {
return fmt.Sprintf("machine error occurred; PC: %#x (%v)", err.PC, err.UnderlyingError)
}
-const DefaultClockRate = time.Microsecond * 10
+const DefaultClockRate ClockRate = 100000 // 100KHz
// Start boots up the machine, with a clock rate of 1 / period
// 10MHz would be expressed as (Microsecond / 10)
-func (m *Machine) Start(period time.Duration) error {
+func (m *Machine) Start(rate ClockRate) error {
if m.stopped != nil {
return errors.New("Machine has already started")
}
@@ -47,7 +49,21 @@ func (m *Machine) Start(period time.Duration) error {
m.cycleCount = 0
m.startTime = time.Now()
go func() {
- ticker := time.NewTicker(period)
+ // we want an acurate cycle counter
+ // Unfortunately, time.NewTicker drops cycles on the floor if it can't keep up
+ // so we need to give ourselves a bit of a buffer. Hopefully 10 is enough
+ ticker := time.NewTicker(rate.ToDuration())
+ cycleChan := make(chan time.Time, 10)
+ go func() {
+ for {
+ if t, ok := <-ticker.C; ok {
+ cycleChan <- t
+ } else {
+ close(cycleChan)
+ break
+ }
+ }
+ }()
scanrate := time.NewTicker(time.Second / 60) // 60Hz
var stoperr error
loop:
@@ -55,7 +71,7 @@ func (m *Machine) Start(period time.Duration) error {
select {
case _ = <-scanrate.C:
m.Video.Flush()
- case _ = <-ticker.C:
+ case _ = <-cycleChan:
if err := m.State.StepCycle(); err != nil {
stoperr = &MachineError{err, m.State.PC()}
break loop
@@ -89,26 +105,58 @@ func (m *Machine) Stop() error {
return err
}
-// EffectiveClockRate returns the current observed rate that the machine
-// is running at, as an average since the last Start()
-func (m *Machine) EffectiveClockRate() uint {
- duration := time.Since(m.startTime)
- cycles := m.cycleCount
- return uint(float64(cycles) / duration.Seconds())
-}
+// ClockRate represents the clock rate of the machine
+type ClockRate int64
-func ClockRateToString(rate uint) string {
+func (c ClockRate) String() string {
+ rate := int64(c)
suffix := "Hz"
- if rate > 1e6 {
+ if rate >= 1e6 {
rate /= 1e6
- suffix = "Mhz"
- } else if rate > 1e3 {
+ suffix = "MHz"
+ } else if rate >= 1e3 {
rate /= 1e3
- suffix = "Khz"
+ suffix = "KHz"
}
return fmt.Sprintf("%d%s", rate, suffix)
}
+func (c *ClockRate) Set(str string) error {
+ var rate int64
+ var suffix string
+ if n, err := fmt.Sscanf(str, "%d%s", &rate, &suffix); err != nil && !(n == 1 && err == io.EOF) {
+ return err
+ }
+ if rate <= 0 {
+ return errors.New("clock rate must be positive")
+ }
+ switch strings.ToLower(suffix) {
+ case "mhz":
+ rate *= 1e6
+ case "khz":
+ rate *= 1e3
+ case "hz", "":
+ default:
+ return errors.New(fmt.Sprintf("unknown suffix %#v", suffix))
+ }
+ *c = ClockRate(rate)
+ return nil
+}
+
+// ToDuration converts the ClockRate to a time.Duration that represents
+// the period of one clock cycle
+func (c ClockRate) ToDuration() time.Duration {
+ return time.Second / time.Duration(c)
+}
+
+// EffectiveClockRate returns the current observed rate that the machine
+// is running at, as an average since the last Start()
+func (m *Machine) EffectiveClockRate() ClockRate {
+ duration := time.Since(m.startTime)
+ cycles := m.cycleCount
+ return ClockRate(float64(cycles) / duration.Seconds())
+}
+
// If the machine has already halted due to an error, that error is returned.
// Otherwise, nil is returned.
// If the machine has not started, an error is returned.
View
42 dcpu/video.go
@@ -1,9 +1,9 @@
package dcpu
import (
- "github.com/kballard/dcpu16/dcpu/core"
"errors"
- "github.com/nsf/termbox-go"
+ "github.com/kballard/dcpu16/dcpu/core"
+ "github.com/kballard/termbox-go"
)
// For the moment, assume an 80 column terminal
@@ -36,10 +36,46 @@ func (v *Video) HandleChanges() {
termWidth, termHeight := termbox.Size()
if row >= termHeight || column >= termWidth {
+ // this is offscreen, so do nothing
return
}
ch := rune(v.words[offset] & 0x7F)
- fg, bg := termbox.ColorDefault, termbox.ColorDefault // figure out colors later
+ // color seems to be in the top 2 nibbles, MSB being FG and LSB are BG
+ // Within each nibble, from LSB to MSB, is blue, green, red, highlight
+ // Lastly, the bit at 0x80 is apparently blink.
+ flag := (v.words[offset] & 0x80) != 0
+ colors := byte((v.words[offset] & 0xFF00) >> 8)
+ fgNibble := (colors & 0xF0) >> 4
+ bgNibble := colors & 0x0F
+ colorToAttr := func(color byte) termbox.Attribute {
+ attr := termbox.ColorDefault
+ // bold
+ if color&0x8 != 0 {
+ attr |= termbox.AttrBold
+ }
+ // cheat a bit here. We know the termbox color attributes go in the
+ // same order as the ANSI colors, and they're monotomically-incrementing.
+ // Just figure out the ANSI code and add ColorBlack
+ ansi := termbox.Attribute(0)
+ if color&0x1 != 0 {
+ // blue
+ ansi |= 0x4
+ }
+ if color&0x2 != 0 {
+ // green
+ ansi |= 0x2
+ }
+ if color&0x4 != 0 {
+ // red
+ ansi |= 0x1
+ }
+ attr |= ansi + termbox.ColorBlack
+ return attr
+ }
+ fg, bg := colorToAttr(fgNibble), colorToAttr(bgNibble)
+ if flag {
+ fg |= termbox.AttrBlink
+ }
termbox.SetCell(column, row, ch, fg, bg)
return
}
View
27 main.go
@@ -1,20 +1,32 @@
package main
import (
+ "flag"
"fmt"
"github.com/kballard/dcpu16/dcpu"
"github.com/kballard/dcpu16/dcpu/core"
- "github.com/nsf/termbox-go"
+ "github.com/kballard/termbox-go"
"io/ioutil"
"os"
)
+var requestedRate dcpu.ClockRate = dcpu.DefaultClockRate
+var printRate *bool = flag.Bool("printRate", false, "Print the effective clock rate at termination")
+
func main() {
- if len(os.Args) != 2 {
- fmt.Fprintf(os.Stderr, "usage: %s program\n", os.Args[0])
+ // command-line flags
+ flag.Var(&requestedRate, "rate", "Clock rate to run the machine at")
+ // update usage
+ flag.Usage = func() {
+ fmt.Fprintf(os.Stderr, "usage: %s [flags] program\n", os.Args[0])
+ flag.PrintDefaults()
+ }
+ flag.Parse()
+ if flag.NArg() != 1 {
+ flag.Usage()
os.Exit(2)
}
- program := os.Args[1]
+ program := flag.Arg(0)
data, err := ioutil.ReadFile(program)
if err != nil {
fmt.Fprintln(os.Stderr, err)
@@ -33,10 +45,11 @@ func main() {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
- if err := machine.Start(dcpu.DefaultClockRate); err != nil {
+ if err := machine.Start(requestedRate); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
+ var effectiveRate dcpu.ClockRate
// now wait for the q key
for {
evt := termbox.PollEvent()
@@ -46,6 +59,7 @@ func main() {
}
if evt.Type == termbox.EventKey {
if evt.Key == termbox.KeyCtrlC || (evt.Mod == 0 && evt.Ch == 'q') {
+ effectiveRate = machine.EffectiveClockRate()
if err := machine.Stop(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
@@ -54,4 +68,7 @@ func main() {
}
}
}
+ if *printRate {
+ fmt.Printf("Effective clock rate: %s\n", effectiveRate)
+ }
}

No commit comments for this range

Something went wrong with that request. Please try again.