@@ -2,7 +2,7 @@ | |||
Collapse OS likely runs from ROM code. If you need to fiddle with your machine | |||
more deeply, you will want to send arbitrary code to it and run it. You can do | |||
so with the shell's `load` and `call` commands. | |||
so with the shell's `poke` and `call` commands. | |||
For example, let's say that you want to run this simple code that you have | |||
sitting on your "modern" machine and want to execute on your running Collapse OS | |||
@@ -22,7 +22,7 @@ Now, we'll send that code to address `0xa000`: | |||
> mptr a000 | |||
A000 | |||
> load 8 (resulting binary is 8 bytes long) | |||
> poke 8 (resulting binary is 8 bytes long) | |||
Now, at this point, it's a bit delicate. To pipe your binary to your serial | |||
connection, you have to close `screen` with CTRL+A then `:quit` to free your | |||
@@ -31,7 +31,7 @@ tty device. Then, you can run: | |||
cat tosend.bin > /dev/ttyUSB0 (or whatever is your device) | |||
You can then re-open your connection with screen. You'll have a blank screen, | |||
but if the number of characters sent corresponds to what you gave `load`, then | |||
but if the number of characters sent corresponds to what you gave `poke`, then | |||
Collapse OS will be waiting for a new command. Go ahead, verify that the | |||
transfer was successful with: | |||
@@ -62,16 +62,19 @@ Success! | |||
The serial connection is not always 100% reliable and a bad byte can slip in | |||
when you push your code and that's not fun when you try to debug your code (is | |||
this bad behavior caused by my logic or by a bad serial upload?). | |||
this bad behavior caused by my logic or by a bad serial upload?). Moreover, | |||
sending contents bigger than `0xff` bytes can be a hassle. | |||
To this end, there is a `upload.py` file in `tools/` that takes care of loading | |||
the file and verify the contents. So, instead of doing `load 8` followed by | |||
your `cat` above, you would have done: | |||
the file and verify the contents. So, instead of doing `mptr a000` followed by | |||
`poke 8` followed by your `cat` above, you would have done: | |||
./upload.py /dev/ttyUSB0 tosend.bin | |||
./upload.py /dev/ttyUSB0 a000 tosend.bin | |||
This emits `load` and `peek` commands and fail appropriately if the `peek` | |||
doesn't match sent contents. Very handy. | |||
This emits `mptr`, `poke` and `peek` commands and fail appropriately if the | |||
`peek` doesn't match sent contents. If the file is larger than `0xff` bytes, | |||
repeat the process until the whole file was sent (file must fit in memory space | |||
though, of course). Very handy. | |||
## Labels in RAM code | |||
@@ -24,41 +24,60 @@ def sendcmd(fd, cmd): | |||
def main(): | |||
parser = argparse.ArgumentParser() | |||
parser.add_argument('device') | |||
parser.add_argument('memptr') | |||
parser.add_argument('filename') | |||
args = parser.parse_args() | |||
try: | |||
memptr = int('0x' + args.memptr, 0) | |||
except ValueError: | |||
print("memptr are has to be hexadecimal without prefix.") | |||
return 1 | |||
if memptr >= 0x10000: | |||
print("memptr out of range.") | |||
return 1 | |||
maxsize = 0x10000 - memptr | |||
st = os.stat(args.filename) | |||
if st.st_size > 0xff: | |||
print("File too big. 0xff bytes max") | |||
if st.st_size > maxsize: | |||
print("File too big. 0x{:04x} bytes max".format(maxsize)) | |||
return 1 | |||
fd = os.open(args.device, os.O_RDWR) | |||
sendcmd(fd, 'poke {:x}'.format(st.st_size).encode()) | |||
print("Poking...") | |||
with open(args.filename, 'rb') as fp: | |||
fcontents = fp.read() | |||
for c in fcontents: | |||
os.write(fd, bytes([c])) | |||
# Let's give the machine a bit of time to breathe. We ain't in a | |||
# hurry now, are we? | |||
time.sleep(0.0001) | |||
print("Poked") | |||
os.read(fd, 5) | |||
print("Peeking back...") | |||
sendcmd(fd, 'peek {:x}'.format(st.st_size).encode()) | |||
peek = b'' | |||
while len(peek) < st.st_size * 2: | |||
peek += os.read(fd, 1) | |||
time.sleep(0.0001) | |||
while True: | |||
fcontents = fp.read(0xff) | |||
if not fcontents: | |||
break | |||
print("Seeking...") | |||
sendcmd(fd, 'mptr {:04x}'.format(memptr).encode()) | |||
os.read(fd, 9) | |||
sendcmd(fd, 'poke {:x}'.format(len(fcontents)).encode()) | |||
print("Poking...") | |||
for c in fcontents: | |||
os.write(fd, bytes([c])) | |||
# Let's give the machine a bit of time to breathe. We ain't in a | |||
# hurry now, are we? | |||
time.sleep(0.0001) | |||
print("Poked") | |||
os.read(fd, 5) | |||
print("Peeking back...") | |||
sendcmd(fd, 'peek {:x}'.format(len(fcontents)).encode()) | |||
peek = b'' | |||
while len(peek) < len(fcontents) * 2: | |||
peek += os.read(fd, 1) | |||
time.sleep(0.0001) | |||
os.read(fd, 5) | |||
print("Got {}".format(peek.decode())) | |||
print("Comparing...") | |||
for i, c in enumerate(fcontents): | |||
hexfmt = '{:02X}'.format(c).encode() | |||
if hexfmt != peek[:2]: | |||
print("Mismatch at byte {}! {} != {}".format(i, peek[:2], hexfmt)) | |||
return 1 | |||
peek = peek[2:] | |||
print("All good!") | |||
memptr += len(fcontents) | |||
print("Done!") | |||
os.close(fd) | |||
print("Got {}".format(peek.decode())) | |||
print("Comparing...") | |||
for i, c in enumerate(fcontents): | |||
hexfmt = '{:02X}'.format(c).encode() | |||
if hexfmt != peek[:2]: | |||
print("Mismatch at byte {}! {} != {}".format(i, peek[:2], hexfmt)) | |||
return 1 | |||
peek = peek[2:] | |||
print("All good!") | |||
return 0 | |||
if __name__ == '__main__': | |||