about summary refs log tree commit diff stats
path: root/tutorial/converter2.mu
blob: ae445239de57778e62eb9a39298c23f131eebc49 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# Temperature Converter app
#   https://eugenkiss.github.io/7guis/tasks/#temp
#
# To build:
#   $ ./translate converter2.mu
# To run:
#   $ qemu-system-i386 code.img

# todo:
#   error checking for input without hard-aborting

fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
  # celsius numeric representation
  var zero: float
  var celsius/xmm1: float <- fahrenheit-to-celsius zero
  # celsius input/display
  var celsius-input-storage: gap-buffer
  var celsius-input/esi: (addr gap-buffer) <- address celsius-input-storage
  initialize-gap-buffer celsius-input, 8/capacity
  value-to-input celsius, celsius-input
  # fahrenheit numeric representation
  var fahrenheit/xmm2: float <- celsius-to-fahrenheit celsius
  # fahrenheit input/display
  var fahrenheit-input-storage: gap-buffer
  var fahrenheit-input/edi: (addr gap-buffer) <- address fahrenheit-input-storage
  initialize-gap-buffer fahrenheit-input, 8/capacity
  value-to-input fahrenheit, fahrenheit-input
  # cursor toggle
  var cursor-in-celsius?/edx: boolean <- copy 0xffffffff/true
  #
  render-title screen
  # event loop
  {
    # render
    render-state screen, celsius-input, fahrenheit-input, cursor-in-celsius?
    render-menu-bar screen
    # process a single keystroke
    $main:input: {
      var key/eax: byte <- read-key keyboard
      var key/eax: grapheme <- copy key
      compare key, 0
      loop-if-=
      # tab = switch cursor between input areas
      compare key, 9/tab
      {
        break-if-!=
        cursor-in-celsius? <- not
        break $main:input
      }
      # enter = convert in appropriate direction
      compare key, 0xa/newline
      {
        break-if-!=
        {
          compare cursor-in-celsius?, 0/false
          break-if-=
          var tmp/xmm0: float <- input-to-value celsius-input
          celsius <- copy tmp
          fahrenheit <- celsius-to-fahrenheit celsius
          value-to-input fahrenheit, fahrenheit-input
          break $main:input
        }
        var tmp/xmm0: float <- input-to-value fahrenheit-input
        fahrenheit <- copy tmp
        celsius <- fahrenheit-to-celsius fahrenheit
        value-to-input celsius, celsius-input
        break $main:input
      }
      # otherwise pass key to appropriate input area
      compare cursor-in-celsius?, 0/false
      {
        break-if-=
        edit-gap-buffer celsius-input, key
        break $main:input
      }
      edit-gap-buffer fahrenheit-input, key
    }
    loop
  }
}

# business logic

fn fahrenheit-to-celsius f: float -> _/xmm1: float {
  var result/xmm1: float <- copy f
  var thirty-two/eax: int <- copy 0x20
  var thirty-two-f/xmm0: float <- convert thirty-two
  result <- subtract thirty-two-f
  var factor/xmm0: float <- rational 5, 9
  result <- multiply factor
  return result
}

fn celsius-to-fahrenheit c: float -> _/xmm2: float {
  var result/xmm1: float <- copy c
  var factor/xmm0: float <- rational 9, 5
  result <- multiply factor
  var thirty-two/eax: int <- copy 0x20
  var thirty-two-f/xmm0: float <- convert thirty-two
  result <- add thirty-two-f
  return result
}

# helpers for UI

fn input-to-value in: (addr gap-buffer) -> _/xmm0: float {
  var s-storage: (stream byte 0x10)
  var s/ecx: (addr stream byte) <- address s-storage
  emit-gap-buffer in, s
  var result/xmm1: float <- parse-float-decimal s
  return result
}

fn value-to-input in: float, out: (addr gap-buffer) {
  var s-storage: (stream byte 0x10)
  var s/ecx: (addr stream byte) <- address s-storage
  write-float-decimal-approximate s, in, 2/decimal-places
  clear-gap-buffer out
  load-gap-buffer-from-stream out, s
}

# helpers for rendering to screen
# magic constants here need to be consistent between functions

fn render-title screen: (addr screen) {
  set-cursor-position screen, 0x1f/x 0xe/y
  draw-text-rightward-from-cursor-over-full-screen screen, " Converter                            ", 0xf/fg 0x16/bg
}

fn render-state screen: (addr screen), c: (addr gap-buffer), f: (addr gap-buffer), cursor-in-c?: boolean {
  clear-rect screen, 0x1f/xmin 0xf/ymin, 0x45/xmax 0x14/ymax, 0xc5/color
  var x/eax: int <- render-gap-buffer screen, c, 0x20/x 0x10/y, cursor-in-c?, 7/fg 0/bg
  x <- draw-text-rightward screen, " celsius = ", x, 0x45/xmax, 0x10/y, 7/fg 0xc5/bg
  var cursor-in-f?/ecx: boolean <- copy cursor-in-c?
  cursor-in-f? <- not
  x <- render-gap-buffer screen, f, x 0x10/y, cursor-in-f?, 7/fg 0/bg
  x <- draw-text-rightward screen, " fahrenheit", x, 0x45/xmax, 0x10/y, 7/fg 0xc5/bg
}

fn render-menu-bar screen: (addr screen) {
  set-cursor-position screen, 0x21/x 0x12/y
  draw-text-rightward-from-cursor-over-full-screen screen, " tab ", 0/fg 0x5c/bg=highlight
  draw-text-rightward-from-cursor-over-full-screen screen, " switch sides ", 7/fg 0xc5/bg
  draw-text-rightward-from-cursor-over-full-screen screen, " enter ", 0/fg 0x5c/bg=highlight
  draw-text-rightward-from-cursor-over-full-screen screen, " convert ", 7/fg 0xc5/bg
}