HNHacker News
TopNewBestAskShowJobs

kruador

114 karma · joined April 24, 2020

submissionscomments
kruador··on 5x5 Pixel font for tiny screens
I remember using Tasword Two for the ZX Spectrum. Even with a 3x7 font (4x8 grid) it only achieves 64 characters per line. Apparently in Tasword III the authors implemented 128 characters per line - by scrolling.

Competitors could apparently show 80 characters per line, but only by switching to a horribly unreadable 2x7 font. "I found the tiny 80 column characters tiring to read but the mode is useful if you want to see what the finished output will be like." - https://www.crashonline.org.uk/31/words.htm . I can't find a screenshot.

kruador··on Treasures found on HS2 route
To be clear, it is currently really easy to find because major earthworks are being done, and that requires space to move in the equipment to do it, along with new roads to get to points that were previously inaccessible, being the middle of nowhere.

To see what it will look like afterwards, try to find High Speed 1, aka the Channel Tunnel Rail Link, now that it's had nearly 20 years to be landscaped and vegetation to grow back. If you don't know what you're looking for, you won't see it.

kruador··on Treasures found on HS2 route
The international platforms are not deleted! They were brought back into use from 2018-2019 to serve the Windsor Lines, which includes the service to Reading - platforms 20-24. That somewhat reduces the congestion at Waterloo; the station throat limits adding more services.

The extension to Euston was supposed to have 11 platforms. Even the reduced scope now being implemented is 6 platforms, I believe. All 11 were required to handle the eastern leg of HS2 [providing bypass capacity for the East Coast Main Line out of King's Cross and the Midland Main Line out of St Pancras], and services to Scotland and Manchester [bypassing the West Coast Main Line from Euston's classic platforms].

kruador··on Transfering Files with gRPC
I would add a further advantage of plain HTTP (REST) compared to gRPC. Splitting the response into blocks and having the client request the next block, as in the gRPC solution, causes round-trip delays. The server can't send the second block of data until the client requests it, so the server is essentially idle until the client has received all packets of the first block, parsed them and generated the next request.

In contrast, while HTTP/2 does impose framing of streams, that framing is done entirely server-side. If all one end has to send to the other is a single stream, it'll be DATA frame after DATA frame for the same stream. The client is not required to acknowledge anything. (At least, nothing above the TCP layer!)

It probably wasn't noticeable in this experiment as, if I'm reading it correctly, the server and client were on the same box, but if you were separated by any significant distance, plain HTTP should be noticeably faster.

kruador··on Uninitialized garbage on ia64 can be deadly (2004)
The 8086 was a stop-gap solution until iAPX432 was ready.

The 80286 was a stop-gap solution until iAPX432 was ready.

The 80386 started as a stop-gap solution until iAPX432 was ready, until someone higher up finally decided to kill that one.

kruador··on Why xor eax, eax?
ARM64 also has fixed length 32-bit instructions. Yes, immediates are normally small and it's not particularly orthogonal as to how many bits are available.

The largest MOV available is 16 bits, but those 16 bits can be shifted by 0, 16, 32 or 48 bits, so the worst case for a 64-bit immediate is 4 instructions. Or the compiler can decide to put the data in a PC-relative pool and use ADR or ADRP to calculate the address.

ADD immediate is 12 bits but can optionally apply a 12-bit left-shift to that immediate, so for immediates up to 24 bits it can be done in two instructions.

ARM64 decoding is also pretty complex, far less orthogonal than ARM32. Then again, ARM32 was designed to be decodable on a chip with 25,000 transistors, not where you can spend thousands of transistors to decode a single instruction.

kruador··on Why xor eax, eax?
ARM64 assembly has a MOV instruction, but for most of the ways it's used, it's an alias in the assembler to something else. For example, MOV between two registers actually generates ORR rd, rZR, rm, i.e. rd := (zero-register) OR rm. Or, a MOV with a small immediate is ORR rd, rZR, #imm.

If trying to set the stack pointer, or copy the stack pointer, instead the underlying instruction is ADD SP, Xn, #0 i.e. SP = Xn + 0. This is because the stack pointer and zero register are both encoded as register 31 (11111). Some instructions allow you to use the zero register, others the stack pointer. Presumably ORR uses the zero register and ADD the stack pointer.

NOP maps to HINT #0. There are 128 HINT values available; anything not implemented on this processor executes as a NOP.

There are other operations that are aliased like CMP Xm, Xn is really an alias for SUBS XZR, Xm, Xn: subtract Xn from Xm, store the result in the zero register [i.e. discard it], and set the flags. RISC-V doesn't have flags, of course. ARM Ltd clearly considered them still useful.

There are other oddities, things like 'rotate right' is encoded as 'extract register from pair of registers', but it specifies the same source register twice.

Disassemblers do their best to hide this from you. ARM list a 'preferred decoding' for any instruction that has aliases, to map back to a more meaningful alias wherever possible.

kruador··on Launch HN: Flywheel (YC S25) – Waymo for Excavators
Toyota's hybrids, at least, have valves in the hydraulic system. If everything is working, the driver's pedal is isolated from the physical pistons. Pressing the pedal instead moves a 'stroke simulator' (a cylinder with a spring in it), and the pressure is measured with a transducer. The Brake ECU tries to satisfy as much braking demand through regenerative braking as possible, applying the rear brakes to keep balance and front brakes if you brake too hard, requesting more braking than can be generated or the battery can absorb.

If there's a failure of the electrical supply to the brake ECU, or another fault condition occurs, various valves then revert to their normally-open or normally-closed positions to allow hydraulic pressure from the pedal through to the brake cylinders, and isolate the stroke simulator.

Because the engine isn't constantly running and providing a vacuum that can be used to assist with brake force, the system also includes a 'brake accumulator' and pump to boost the brake pressure.

Reference: https://pmmonline.co.uk/technical/blue-prints-insight-into-t...

I don't know for certain, but I would assume that other hybrids and EVs have similar systems to maximise regenerative braking.

kruador··on My new Git utility `what-changed-twice` needs a new name
It's a pain in the backside to run on Windows, for two reasons. Firstly, Windows doesn't have (by default) a lot of the tools that are preinstalled in most nix environments. Git for Windows ships half a Cygwin distribution (MSYS2) including Bash, Perl, and Tcl.

Second, Windows doesn't really have a 'fork' API. Creating a new process on Windows is a heavyweight operation compared to nix. As such, scripts that repeatedly invoke other commands are sluggish. Converting them to C and calling plumbing commands in-process has a radical effect on performance.

Git for Windows is more of a maintained fork than a real first-class platform.

Also, I believe it's a goal to make it possible to use Git as a library rather than as an executable. That's hard to do if half the logic is in a random scripting language. Library implementations exist - notably libgit2 - but it can never be fully up to date with the original. Search for 'git libification'.

Many IDEs started their Git integration with libgit2, but subsequently fell foul of things that libgit2 can't do or does inconsistently. Therefore they fall back on executing `git` with some fixed-format output.

kruador··on Betty Crocker broke recipes by shrinking boxes
The text that the footnote is attached to is:

"Large Language Models can gall on an aesthetic level because they are IMPish slurries of thought itself, every word ever written dried into weights and vectors and lubricated with the margarine of RLHF." I infer 'IMPish' as meaning 'like Instant Mashed Potato'.

I read that footnote as a somewhat oblique criticism of two LLMs, rather than on the statistic itself - which may indeed have just been fabricated by the LLM as opposed to an actual statistic somehow dredged from its training data, or pulled from a web search.

kruador··on Betty Crocker broke recipes by shrinking boxes
Strictly, UK teaspoons are 5 ml and tablespoons 15 ml. The metric tablespoons already used in Europe were probably close enough to half an Imperial fluid ounce for it not to matter for most purposes.

My kids' baby bottles were labelled with measurements in metric (30 ml increments) and in both US and Imperial fluid ounces. The cans of formula were supplied with scoops for measuring the powder, which were also somewhere close to 2 tablespoons/one fluid ounce (use one scoop per 30 ml of water). There are dire warnings about not varying the concentration from the recommended amount, but I assume that it's not really that precise within 1-2% - more about not varying by 10-20%. My kids seem to have survived, anyway.

kruador··on Betty Crocker broke recipes by shrinking boxes
Blame the European regulators who decided that it was no longer necessary to have standard pack sizes.

Pack sizes were regulated in 1975 for volume measures (wine, beer, spirits, vinegar, oils, milk, water, and fruit juice) and in 1980 for weights (butter, cheese, salt, sugar, cereals [flour, pasta, rice, prepared cereals], dried fruits and vegetables, coffee, and a number of other things). In 2007, all of that was repealed - and member states were now forbidden from regulating pack sizes!

I think the rationale was that now the unit price (price per unit of measurement) was mandatory to display, consumers would still know which of two different packs on the same shelf was better value. But standard pack sizes don't just provide value-for-money comparisons, as this article shows.

kruador··on Let's Learn x86-64 Assembly (2020)
It wasn't possible on the 386. Ken Shirriff discusses how the Intel 80386's register file was built at https://www.righto.com/2025/05/intel-386-register-circuitry..... Only four of the registers are built to allow 32-, 16- or 8-bit writes. Reads output the entire register onto the bus and the ALU does the appropriate masking. The twist is for the legacy 16-bit upper half-registers - themselves really a legacy of the 8080, and the requirement to be able to directly translate 8080 code opcode-for-opcode. The output of these has to be shifted down 8 bits to be in the right place for the ALU, then these bits have to be selected.

AMD seem to have decided to regularise the instruction set for 64-bit long mode, making all the registers consistently able to operate as 64-bit, 32-bit, 16-bit, and 8-bit, using the lowest bits of each register. This only occurs if using a REX prefix, usually to select one of the 8 additional architectural registers added for 64-bit mode. To achieve this, the bits that are used to select the 'high' part of the legacy 8086 registers in 32- or 16-bit code (and when not using the REX prefix) are used instead to select the lowest 8 bits of the index and pointer registers.

From the "Intel 64 and IA-32 Architectures Software Developer's Manual":

"In 64-bit mode, there are limitations on accessing byte registers. An instruction cannot reference legacy high-bytes (for example: AH, BH, CH, DH) and one of the new byte registers at the same time (for example: the low byte of the RAX register). However, instructions may reference legacy low-bytes (for example: AL, BL, CL, or DL) and new byte registers at the same time (for example: the low byte of the R8 register, or RBP). The architecture enforces this limitation by changing high-byte references (AH, BH, CH, DH) to low byte references (BPL, SPL, DIL, SIL: the low 8 bits for RBP, RSP, RDI, and RSI) for instructions using a REX prefix."

In 64-bit code there is very little reason at all to be using bits 15:8 of a longer register.

This possibly puts another spin on Intel's desire to remove legacy 16- and 32-bit support (termed 'X86S'). It would remove the need to support AH, BH, CH and DH - and therefore some of the complex wiring from the register file to support the shifting. If that's what it currently does.

Actually, looking at Agner Fog's optimisation tables (https://www.agner.org/optimize/instruction_tables.pdf) it appears there is significant extra latency in using AH/BH/CH/DH, which suggests to me that the processor actually implements shifting into and out of the high byte using extra micro-ops.

kruador··on Donkey Kong Country 2 and Open Bus
No, SDRAM means Synchronous DRAM, where the data is clocked out of the DRAM chips instead of just appearing on the bus some time after the Column Address Strobe is asserted. Clocking it means that the data doesn't appear before the CPU (or other bus master) is ready to receive it, and that it doesn't disappear before the CPU has read it.

Static RAM (SRAM) is a circuit that retains its data as long as the power is supplied to it. Dynamic RAM (DRAM) must be refreshed frequently. It's basically a large array of tiny capacitors which leak their stored charge through imperfect transistor switches, so a charged capacitor must be regularly recharged. You would think that you would need to read the bit and rewrite its value in a second cycle, but it turns out that reading the value is itself a destructive operation and requires the chip to internally recharge the capacitors.

Further, the chip is organised in rows and columns - generally there are the same number of Sense Amplifiers as columns, with a whole row of cells discharging into their corresponding Sense Amplifiers on each read cycle, the Sense Amplifiers then being used to recharge that row of cells. The column signals select which Sense Amplifier is connected to the output. So you don't need to read every row and column of a chip, just some column on every row. The Sense Amplifier is a circuit that takes the very tiny charge from the cell transistor and brings it up to a stable signal voltage for the output.

So why use DRAM at all if it has this need to be constantly refreshed? Because the Static RAM circuit requires 4-6 transistors per cell, while DRAM only requires 1. You get close to 4-6 times as much storage from the same number of transistors.

kruador··on Donkey Kong Country 2 and Open Bus
The Sinclair ZX80 and ZX81 have static RAM internally, which you wouldn't expect for a) a computer that's designed to be as cheap as possible and b) uses a Zilog Z80 which has built-in refresh circuitry.

The reason is that the designers saved a few chips by repurposing the Z80's refresh circuit as a counter/address generator, when generating the video signal. Specifically, it uses the instruction fetch cycle to read the character code from RAM, then it uses the refresh cycle to read the actual line of character data from the ROM. The ZX80 nominally clocks the Z80 at 3.25MHz, but a machine cycle is four clocks (two for fetch, two for refresh), so it's effectively the same speed as a 0.8125 MHz 6502.

I wrote a long section here about how the ZX80 uses the CPU to generate the screen and the extra logic that involves, but it was getting too long :) The ZX81 is basically just a cost-reduced ZX80 where all the discrete logic chips are moved into one semi-custom chip.

Doing this makes external RAM packs more expensive too. You couldn't use the real refresh address coming from the Z80 because the video generator would be hopping around a small range of addresses in the ROM, rather than covering the whole of RAM (or at least each row of the DRAM). The designer has two options:

1. Use static RAM in the external RAM pack, making it substantially more expensive for the RAM itself; 2. Use DRAM in the external RAM pack, and add extra refresh circuitry to refresh the DRAM when the main computer is using the refresh cycle doing its video madness.

I think most RAM packs did the second option.

kruador··on A leap year check in three instructions
Most 8-bit CPUs didn't even have a hardware multiply instruction. To multiply on a 6502, for example, or a Z80, you have to add repeatedly. You can multiply by a power of 2 by shifting left, so you can get a bigger result by switching between shifting and adding or subtracting. Although, again, on these earlier CPUs you can only shift by one bit at a time, rather than by a variable number of bits.

There's also the difference between multiplying by a hard-coded value, which can be implemented with shifts and adds, and multiplying two variables, which has to be done with an algorithm.

The 8086 did have multiply instructions, but they were implemented as a loop in the microcode, adding the multiplicand, or not, once for each bit in the multiplier. More at https://www.righto.com/2023/03/8086-multiplication-microcode.... Multiplying by a fixed value using shifts and adds could be faster.

The prototype ARM1 did not have a multiply instruction. The architecture does have a barrel shifter which can shift one of the operands by any number of bits. For a fixed multiplication, it's possible to compute multiplying by a power of two, by (power of two plus 1), or by (power of two minus 1) in a single instruction. The latter is why ARM has both a SUB (subtract) instruction, computing rd := rs1 - Operand2, and a RSB (Reverse SuBtract) instruction, computing rd := Operand2 - rs1. The second operand goes through the barrel shifter, allowing you to write an instruction like 'RSB R0, R1, R1, #4' meaning 'R0 := (R1 << 4) - R1', or in other words '(R1 * 16) - R1', or R1 * 15.

ARMv2 added in MUL and MLA (MuLtiply and Accumulate) instructions. The hardware ARM2 implementation uses a Booth's encoder to multiply 2 bits at a time, taking up to 16 cycles for 32 bits. It can exit early if the remaining bits are all 0s.

Later ARM cores implemented an optional wider multiplier (that's the 'M' in 'ARM7TDMI', for example) that could multiply more bits at a time, therefore executing in fewer cycles. I believe ARM7TDMI was 8-bit, completing in up to 4 cycles (again, offering early exit). Modern ARM cores can do 64-bit multiplies in a single cycle.

kruador··on 20 years of Git
This is, in some ways, reintroducing something that other source control systems forced on you (and you can see it in one of the videos that Scott linked, about using BitKeeper - Ep.4 Bits and Booze, https://www.youtube.com/watch?v=MPFgOnACULU). The previous tools I used (SourceGear Vault, MS Team Foundation Services) required you to have a separate working tree for each branch - the two were directly tied together. That's sometimes useful if you need to have the two versions running concurrently, but for short-lived topic branches or, as you say, working on multiple topics at the same time, it can be very inconvenient.

Initially it was jarring to not get a different working directory for each branch, but I soon got used to it. Working in the same directory for multiple branches means that untracked files stay around - can be helpful for things like IDE workspace configuration, which is specific to me and the project, but not the branch.

You can of course have multiple clones of the repository - even clones of clones - but pushing/pulling branches from one to another is a lot more work than just checking out a branch in a different worktree.

My general working practice now is to keep release versions in their own worktree, and using the default worktree (where the .git directory lives) for development on the main branch. That means I don't need to keep resyncing up my external dependencies (node_modules, for example) when switching between working on different releases. But I can see a good overview of my branches, and everything on the remote, from any worktree.

kruador··on US Administration announces 34% tariffs on China, 20% on EU
I've seen a suggestion that they're using ccTLDs.

Which might explain why the British Indian Ocean Territory - population, one US military base - has such a high tariff. The BIOT, aka Diego Garcia, has the ccTLD .io.

kruador··on Git clone –depth 2 is vastly better than –depth 1 if you want to Git push later
I can't replicate the initial problem, at least pushing to Bitbucket. I'm using Windows, so I didn't use `touch` - instead I used 'echo' to create a new file in a shallow clone of my repo. That repo is 126 MB on Bitbucket, and the shallow clone downloaded 6395 objects taking 40.68 MB.

I've tried with a new file both having content ('Test shallow clone push'), and again with an empty file. In both cases it pushed 3 objects, and in the empty file case it reused one (it turns out my repo already has some empty files in it).

It's always possible that this is (or was) a GitHub bug - I haven't tried it there.

kruador··on Git clone –depth 2 is vastly better than –depth 1 if you want to Git push later
See my top-level response, but basically nothing is mangled. Instead Git internally treats it as a 'graft' and knows not to look for parents of the prior commit.

I started that comment as a reply to you but I realised that a) it may just have been a bug that might already be fixed and b) it looks like the Stack Overflow answer was speculative and not tested!

kruador··on Git clone –depth 2 is vastly better than –depth 1 if you want to Git push later
It isn't mangled. The commit is there as-is. Instead the repository has a file, ".git/shallow", which tells it not to look for the parents of any commit listed there. If you do a '--depth 1' clone, the file will list the single commit that was retrieved.

This is similar to the 'grafts' feature. Indeed 'git log' says 'grafted'.

You can test this using "git cat-file -p" with the commit that got retrieved, to print the raw object.

> git clone --depth 1 https://github.com/git/git > git log

commit 388218fac77d0405a5083cd4b4ee20f6694609c3 (grafted, HEAD -> master, origin/master, origin/HEAD) Author: Junio C Hamano <gitster@pobox.com> Date: Mon Feb 10 10:18:17 2025 -0800

    The ninth batch

    Signed-off-by: Junio C Hamano <gitster@pobox.com>
> git cat-file -p 388218fac77d0405a5083cd4b4ee20f6694609c3

tree fc620998515e75437810cb1ba80e9b5173458d1c parent 50e1821529fd0a096fe03f137eab143b31e8ef55 author Junio C Hamano <gitster@pobox.com> 1739211497 -0800 committer Junio C Hamano <gitster@pobox.com> 1739211512 -0800

The ninth batch

Signed-off-by: Junio C Hamano <gitster@pobox.com>

I can't reproduce the problem pushing to Bitbucket, using the most recent Git for Windows (2.47.1.windows.2). It only sent 3 objects (which would be the blob of the new file, the tree object containing the new file, and the commit object describing the tree), not the 6000+ in the repository I tested it on.

It may be that there was a bug that has now been fixed. Or it may be something that only happens/happened with GitHub (i.e. a bug at the receiving end, not the sending one!)

I note that the Stack Overflow user who wrote the answer left a comment underneath saying

"worth noting: I haven't tested this; it's just some simple applied math. One clone-and-push will tell you if I was right. :-)"

kruador··on Undergraduate shows that searches within hash tables can be much faster
It was designed to support a specific Air Force requirement: the ability to launch, release or capture a spy satellite, then return to (approximately) the same launch site, all on a single orbit. (I say 'approximately' because a West Coast launch would have been from Vandenberg Air Force Base, returning to Edwards Air Force Base.)

The cargo bay was sized for military spy satellites (imaging intelligence) such as the KH-11 series, which may have influenced the design of the Hubble Space Telescope. Everything else led on from that.

Without those military requirements, Shuttle would probably never have got funded.

I'm listening to "16 Sunsets", a podcast about Shuttle from the team that made the BBC World Service's "13 Minutes To The Moon" series. (At one point this was slated to be Season 3, but the BBC dropped out.) https://shows.acast.com/16-sunsets/episodes/the-dreamers covers some of the military interaction and funding issues.

kruador··on Who would buy a Raspberry Pi for $120?
Actually $9 more to go from 2GB to 4GB, just for the RAM chip itself. See my reply above.

32GB is impossible at present. No-one makes a 256 gigabit (32x8) chip to fit in the same footprint, so then you have to do a new board design to support multiple chips, which might not be doable in the board's current footprint.

Micron do a 192 gigabit (=24GB) chip but it's LPDDR5, rather than LPDDR4x, which I imagine the RPi 5's SoC can't drive.

kruador··on Who would buy a Raspberry Pi for $120?
The problem is that to fit on the existing board, you need a single RAM package with the same number and function of pads. Looking at a sample photo of a board, I can see it has a Micron package labelled D8CJN. Looking that up on Micron's FBGA parts decoder, the full product code is MT53E2G32D4DE-046 WT:C which is a 64Gb (64 gigabit, i.e. 8 gigabyte) LPDDR4x RAM at 2133 MHz, in a 200-ball TFBGA package.

Looking at Micron's range, they now have a 128Gb (16GB) chip with matching specs, which is the MT53E4G32D8CY-046 WT:C. That chip costs £74.66 per chip from Mouser Electronics (just one site I found selling it) in quantities of 1,360.

In contrast the 2GB chip - which I surmise is a MT53E512M32D1ZW-046 - costs £9.26 each in quantities of 250. There's a price break listed for 2,720 of the things, but I'm not going to ask for a quote.

So that's £65.40 more for the larger RAM chip. Which is about $80 on today's mid-market exchange rates.

Looking at the 4GB it's probably a MT53E1G32D2FW-046, which is £16.72 in bulk quantities of 1,360 from the same wholesaler (for the revision C part). So that's an extra £7.46 or about $9.

Why are other systems' RAM upgrades cheaper? Largely because they use more chips, and therefore each chip has less capacity. That means either the chip is less dense, or it's a smaller die, so fewer defects are likely within the one chip, making yields higher. An SO-DIMM might have two or four chips on it, depending on whether it's populated on one side or both; a full-size DIMM might be 8 or 16 (or 9 or 18 if it has ECC).

Obviously I've made the assumption that this is just a drop-in replacement chip on the existing board. But I assume it would be very hard to redesign the board to fit in the same form factor, with all connectors in the same positions, but add on another RAM chip. If the SoC even supports driving more than one RAM chip, which it may not.

TL;DR that's what Micron charge for the larger chip.

kruador··on In Search of a Faster SQLite
SQLite is in-process. It never spins up another process or thread. It's just a library. Its blocking I/O means that the thread that called into SQLite can't do anything else until it completes. Though note that SQLite's underlying API is essentially a row-by-row interface - you run a query by calling sqlite3_step(), which returns when the next row has been retrieved.

SQLite does have a page cache, so recently-accessed pages will still be in the cache, allowing for the next result to frequently be returned without stalling. And the operating system's file cache may be reading ahead if it detects a sequential access pattern, so the data may be available to SQLite without blocking even before it requests it. (SQLite defaults to 1KB pages, but the OS may well perform a larger physical read than that into its cache anyway.)

Asynchronous I/O usually isn't actually any faster to complete. Indeed there might be more overhead. The benefit is that you can have fewer threads, if you architect your server around asynchronous I/O. That saves memory on thread stacks and other thread-specific storage. It can also reduce thrashing of CPU cache and context switch overhead, which can be an issue if too many threads are runnable at the same time (i.e. more threads than you have CPU cores.) It might also reduce user/kernel mode transitions.

kruador··on Story of the two thousand stolen Playdate handhelds
If you look at the map, Shipfusion is 4350 North 5th Street, North Las Vegas. The Circle K is literally next door, on the intersection of North 5th Street and East Craig Road. See https://maps.app.goo.gl/Wz6iNaViGf3ToM1U6.

I think what happened here is that the FedEx driver was in such a hurry to drop off that he saw a bunch of people apparently outside Shipfusion, stopped and said 'Hey, delivery for you', rather than going and finding the proper entrance to the loading dock. And to be fair, that's the corner with the company logo on it - looking at Google Streetview I can't see a logo at the back of the building where the loading dock is!

And to be even more fair to the driver, they are often given utterly unrealistic amounts of time to drive to the next drop, or to complete the drop. They can only complete their rounds by cutting corners.

kruador··on How DRAM changed the world
Indeed - the 'dynamic' comes from 'dynamic logic'. Wikipedia: "It is distinguished from the so-called static logic by exploiting temporary storage of information in stray and gate capacitances." What Dennard realised was that you don't actually need to have a separate capacitor to hold the bit value - the bit value is just held on the stray and gate capacitance of the transistor that switches on when that bit's row and column are selected, causing the stray capacitance to discharge through the output line.

Because of that, the act of reading the bit's value means that the data is destroyed. Therefore one of the jobs of the sense amplifier circuit - which converts the tiny voltage from the bit cell to the external voltage - is to recharge the bit.

But that stray capacitance is so small that it naturally discharges through the high, but not infinite, resistance when the transistor is 'off'. Hence, you have to refresh DRAM, by regularly reading every bit frequently enough that it hasn't discharged before you got to it. Usually you might only need to read every row frequently enough, because there's actually a sense amplifier for each column, reading all the bit values in that row, with the column address strobe just selecting which column bit gets output.

kruador··on Solving the Mystery of ARM7TDMI Multiply Carry Flag
The reason was because it makes subroutine return and stack frame cleanup simpler.

You know this, but background for anyone else:

ARM's subroutine calling convention places the return address in a register, LR (which is itself a general purpose register, numbered R14). To save memory cycles - ARM1 was designed to take advantage of page mode DRAM - the processor features store-multiple and load-multiple instructions, which have a 16-bit bitfield to indicate which registers to store or load, and can be set to increment or decrement before or after each register is stored or loaded.

The easy way to set up a stack frame (the way mandated by many calling conventions that need to unwind the stack) is to use the Store Multiple, Decrement Before instruction, STMDB. Say you need to preserve R8, R9, R10:

STMDB R8-R10, LR

At the end of the function you can clean up the stack and return in a single instruction, Load Multiple with Increment After:

LDMIA R8-R10, PC

This seemed like a good decision to a team producing their first ever processor, on a minimal budget, needing to fit into 25,000 transistors and to keep the thermal design power cool enough to use a plastic package, because a ceramic package would have blown their budget.

Branch prediction wasn't a consideration as it didn't have branch prediction, and register pressure wasn't likely a consideration for a team going from the 3-register 6502, where the registers are far from orthogonal.

Also, it doesn't waste instruction space: you already need 4 bits to encode 14 registers, and it means that you don't need a 'branch indirect' instruction (you just do MOV PC,Rn) nor 'return' (MOV PC,LR if there's no stack frame to restore).

There is a branch instruction, but only so that it can accommodate a 24-bit immediate (implicitly left-shifted by 2 bits so that it actually addresses a 26-bit range, which was enough for the original 26-bit address space). The MOV immediate instruction can only manage up to 12 bits (14 if doing a shift-left with the barrel shifter), so I can see why Branch was included.

Indeed, mentioning the original 26-bit address space: this was because the processor status flags and mode bits were also available to read or write through R15, along with the program counter. A return (e.g. MOV PC,LR) has an additional bit indicating whether to restore the flags and processor state, indicated by an S suffix. If you were returning from an interrupt it was necessary to write "MOVS PC, LR" to ensure that the processor mode and flags were restored.

# It was acceptable in the 80s', It was acceptable at the time... #

Ken Shirriff has a great article "Reverse engineering the ARM1" at https://www.righto.com/2015/12/reverse-engineering-arm1-ance....

Getting back to multipliers:

ARM1 didn't have a multiply instruction at all, but experimenting with the ARM Evaluation System (an expansion for the BBC Micro) revealed that multiplying in software was just too slow.

ARM2 added the multiply and multiply-accumulate instructions to the instruction set. The implementation just used the Booth recoding, performing the additions through the ALU, and took up to 16 cycles to execute. In other words it only performed one Booth chunk per clock cycle, with early exit if there was no more work to do. And as in your article, it used the carry flag as an additional bit.

I suspect the documentation says 'the carry is unreliable' because the carry behaviour could be different between the ARM2 implementation and ARM7TDMI, when given the same operands. Or indeed between implementations of ARMv4, because the fast multiplier was an optional component if I recall correctly. The 'M' in ARM7TDMI indicates the presence of the fast multiplier.

kruador··on Alexa is in millions of households and Amazon is losing billions
No, they aren't. GS1 - the organisation that administers retail barcodes - has rules about when to change the Global Trade Item Number for a product. The GTIN is what you probably mean by 'UPC' but GS1 now distinguishes between the abstract product number (the GTIN), and the concrete barcode symbology containing a 12-digit GTIN (UPC-A).

The Guiding Principles are:

* Is a consumer and/or trading partner expected to distinguish the changed or new product from previous/current products?

* Is there a regulatory/liability disclosure requirement to the consumer and/or trading partner?

* Is there a substantial impact to the supply chain (e.g., how the product is shipped, stored, received)?

- from https://www.gs1.org/1/gtinrules/en/guiding-principles

kruador··on Alexa is in millions of households and Amazon is losing billions
The UK switched from selling petrol in gallons to litres in the 1980s. I think I just about recall petrol prices suddenly changing dramatically when I was fairly young - I used to help my Dad keep records of how much fuel we'd bought at what price. I'd write down the figures in the book while he went to pay (it was always self-service) so I must have been old enough to be left alone for 5 minutes!

This Energy Institute statistical series - https://knowledge.energyinst.org/search/record?id=58969 - says that their records changed from "new pence per gallon" to "new pence per litre" at the start of 1989. That seems late for my recollection.

Looking back at historical data from https://www.gov.uk/government/statistical-data-sets/oil-and-..., it appears that the average price for "4 star" petrol (97 RON) crossed the £1 per gallon threshold some time in 1979 (Table 4.1.3, and multiply by 4.54609). I'm not old enough to remember that!

By 1989, prices were at 168.8 pence per litre (i.e. £1.68). So I think the story about the change being because it had gone over £1 per gallon has to be a myth. However, retailers certainly weren't complaining about the price displayed being less than one quarter of what it had been! In contrast, they were much less happy about prices per kilogram being more than twice the price per pound (weight).

Prices crossed £1 per litre for 'Premium Unleaded' (95 RON) in November 2007. They fell back below this level in November 2008 but went back up over it in June 2009.

Page 1 of 2Next →